首页/开发/系统分析与架构设计速查/聚合与聚合根(Aggregate)
需求分析与建模61 条

聚合与聚合根(Aggregate)

聚合是一致性边界:外部只能通过聚合根访问内部实体与值对象,一次事务只修改一个聚合;跨聚合一致性通过领域事件实现最终一致。

聚合是什么

聚合是一致性边界:一组对象在被修改时必须保持业务不变量,外部只能通过聚合根访问内部实体与值对象,一次事务只修改一个聚合。聚合内部用数据库事务保证强一致,聚合之间通过领域事件达成最终一致。划分聚合的判据是真需要原子保证的不变量,而不是对象之间的引用关系;把大量实体塞进一个聚合会带来锁竞争、加载开销与并发冲突。

基本信息

聚合根聚合的唯一访问入口,负责维护内部不变量
设计原则聚合尽量小;跨聚合用 ID 引用而非对象引用;一次事务只改一个聚合
一致性聚合内强一致由事务保证,聚合间最终一致由事件驱动
标识聚合根有全局唯一标识,内部实体只保证聚合内唯一
持久化仓储以聚合为粒度加载与保存,不单独保存内部实体

不变量示例

不变量示例
class Order {                 # 聚合根
  addLine(sku, qty) {
    require(status == 草稿)      # 不变量一:仅草稿状态可改明细
    require(lines.size < 100)    # 不变量二:明细行数上限
    require(qty > 0)             # 不变量三:数量必须为正
  }
  submit() { require(lines 非空) }   # 提交前必须至少有一行
}
# 跨聚合只保存 ID 引用,不持有其他聚合对象

典型使用场景

  • 订单与明细:明细行必须与订单一起满足不变量,适合同一聚合
  • 库存扣减:库存独立成聚合,通过事件与订单协作而非同事务修改
  • 账号与地址:地址是值对象时随账号修改,作为独立实体时另立聚合
  • 高并发写入优化:把热点数据移出大聚合,降低锁竞争
  • 事件驱动集成:聚合内变更发出领域事件,由其他聚合订阅处理
  • 重构旧模型:按事务边界重新审视对象图,识别真正的聚合

聚合边界判断

信号建议原因
必须同时成立的不变量放在同一聚合需要事务保证原子性
仅需最终一致拆成不同聚合用事件协作即可,减少锁范围
只有一个字段被并发修改考虑独立聚合避免整聚合加锁
加载一个聚合要读上千行拆小聚合或用延迟加载加载与内存开销过大
外部经常绕过根直接改内部检查边界是否划错说明真实不变量不在根上
聚合根通常不负责查询:复杂列表与统计查询走独立的读模型,避免为了展示需求把聚合加载成巨大的对象图。
跨聚合用一个数据库事务同时修改多个聚合,会重新引入耦合与锁竞争;若确实无法拆分,说明边界划分需要重新评估。

易错点

  • 把所有关联实体塞进一个大聚合,加载慢、锁范围大,并发写入频繁冲突
  • 聚合之间用对象引用而非 ID 引用,一处加载牵连整张对象图
  • 一个事务修改多个聚合,跨聚合强耦合且变更互相阻塞
  • 不变量写在应用服务里而不是聚合内,绕过入口的代码路径能破坏规则
  • 仓储以表为单位而非以聚合为单位保存,内部实体出现半提交状态

数据来源与参考

  • Eric Evans《Domain-Driven Design》
  • Vaughn Vernon《Effective Aggregate Design》(2011)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌