DDD建模中判定聚合根与子实体归属的最佳实践咨询
聚合根与子实体判断核心依据及场景解决方案
一、判断实体归属的核心标准
- 业务生命周期一致性:如果实体的存在完全依附于另一实体,主实体删除时该实体必须同步级联删除,且无脱离主实体单独存在的业务场景,优先作为子实体;反之则考虑独立为聚合根。
- 事务与并发约束:DDD要求单次事务仅修改一个聚合根,若实体的修改频率极高、并发量大,放入主聚合会导致主聚合频繁出现并发冲突,应当独立为聚合根降低锁粒度。
- 全局引用需求:如果实体需要被其他业务场景直接通过全局唯一ID访问,而非必须通过主聚合导航获取,必须独立为聚合根——子实体仅在所属聚合内有唯一标识,不支持外部直接引用。
- 业务规则耦合度:如果实体的自身业务校验不需要和主聚合的属性联动校验,可以独立维护自身的不变性规则,可考虑独立为聚合根;如果实体的操作必须关联主聚合的状态、属性做联合校验,适合放在主聚合内作为子实体。
二、Meeting案例的设计逻辑说明
你提到的案例中两个实体的归属设计完全符合上述标准:
MeetingAttendee作为Meeting的子实体,是因为参会者的添加、删除必须关联Meeting的属性做联合校验,比如是否在报名截止时间前、参会人数是否超出会议最大容量,且会议取消时参会资格自动失效,生命周期完全依附于会议,所以放在Meeting聚合内统一管理。MeetingComment独立为聚合根,核心原因有两点:- 评论的增删改操作几乎不需要和会议的核心属性做联动校验,最多仅需校验会议是否已删除,该校验通过最终一致性即可实现,不需要放在同一个事务里;
- 评论的并发操作频率远高于会议核心信息的修改频率,同时评论本身有单独被引用的场景(比如回复评论、举报评论、点赞评论),如果放在
Meeting聚合内,每次操作评论都要锁整个会议聚合,会严重拉高并发冲突概率,影响其他会议操作的性能。
你之前的猜测方向是正确的,是否可选并不是核心判断依据,主聚合完全可以包含可选的子实体列表。
三、Game场景的建模建议
你可以对照上述4个核心标准,结合实际业务需求选择:
- 如果
GamePlayer的操作需要和Game的状态强绑定校验,比如添加选手必须校验Game未开赛、参赛人数未超出上限,添加受击记录GamePlayerHits必须校验Game处于进行中,且赛事相关操作并发量不高、没有直接通过ID访问单个GamePlayer的需求,完全可以保留当前设计,将GamePlayer作为Game的子实体管理,Game初始状态下选手列表为空也完全符合聚合设计要求。 - 如果存在以下任意一种场景,建议将
GamePlayer独立为聚合根,仅保留对Game的ID引用:- 有直接通过
GamePlayerID查询、操作的业务需求,比如统计特定选手的所有参赛受击数据、单独给选手颁发赛事奖项等; - 赛事进行过程中
GamePlayerHits的写入并发量极高,放在Game聚合内会导致频繁的并发冲突; - 单场赛事的参赛选手量级极大,每次加载
Game聚合都要拉取全量选手和受击数据会严重影响性能。
- 有直接通过
内容的提问来源于stack exchange,提问作者DJH2006X
相关产品推荐
相关产品推荐

