4. 战术设计:聚合、聚合根与领域事件

战术设计是领域驱动设计(DDD)从战略蓝图落地到代码实现的关键桥梁。本章聚焦于三个核心构造块:聚合(Aggregate)、聚合根(Aggregate Root)和领域事件(Domain Event)。它们共同定义了业务逻辑的封装边界、数据一致性保障以及跨模块的通信机制,是微服务拆分和接口版本迁移的微观基础。

4.1 聚合与聚合根:一致性边界与入口

聚合是一组相关领域对象的组合,被视为一个整体进行数据变更。聚合根是聚合中唯一允许外部对象引用的实体,负责维护聚合内部的不变量。

核心原则:外部对象只能通过聚合根访问聚合内的任何元素。聚合内部的所有操作必须保证最终一致性,聚合之间通过领域事件或应用服务进行协调。

4.1.1 聚合设计三要素

要素 说明 示例(订单上下文)
聚合根 全局唯一标识,外部引用的唯一入口 Order(订单ID)
内部实体 在聚合内拥有本地标识,但对外不可见 OrderItem(行项目ID)
值对象 描述属性,无标识,不可变 Address、Money

4.1.2 聚合边界划分原则

  • 一致性优先:在一次事务中必须保持强一致性的对象应放在同一个聚合内。
  • 边界最小化:聚合应尽量小,只包含真正需要一致性的对象,避免大聚合导致性能瓶颈。
  • 引用通过ID:聚合之间通过聚合根ID引用,而非直接持有对象引用。
实践建议:在接口版本迁移中,聚合根ID是版本路由的关键参数。保持聚合根ID的稳定性,可以降低下游消费者因内部重构而被迫升级接口版本的风险。
聚合与聚合根结构 聚合边界(Order Aggregate) 聚合根:Order orderId: UUID 实体:OrderItem itemId: UUID (本地) 值对象:Address 不可变,无标识 外部服务/聚合 通过 orderId 引用 引用聚合根ID

4.2 领域事件:跨聚合的最终一致性通信

领域事件是领域模型中发生的、业务人员关心的、具有业务意义的事件。它用于在聚合之间传递状态变更信息,是实现最终一致性和事件溯源的核心机制。

4.2.1 领域事件的生命周期

  1. 产生:聚合根在执行命令后,内部生成领域事件。
  2. 发布:在聚合根状态持久化后(事务提交前或后),事件被发布到事件总线。
  3. 处理:订阅该事件的处理器(可能位于同一模块或不同模块)执行后续逻辑。
  4. 存储:事件通常被持久化到事件存储中,用于审计、溯源或补偿。

4.2.2 领域事件 vs. 集成事件

特性 领域事件 集成事件
作用域 同一限界上下文内 跨限界上下文或微服务
通信方式 内存事件总线(进程内) 消息队列(进程间)
一致性要求 最终一致性(通常在同一事务边界内) 最终一致性(分布式事务)
版本迁移影响 事件结构变更影响同一模块内部 事件结构变更需版本协商,影响外部消费者
版本迁移陷阱:领域事件的结构变更(如新增字段、修改字段类型)是接口版本迁移的隐形炸弹。建议为每个领域事件定义明确的版本号(如 OrderSubmittedV1),并在事件处理器中支持多版本兼容。
领域事件流转与处理 命令 submitOrder(cmd) 聚合根 Order.submit() 领域事件 OrderSubmitted 事件处理器 SendEmailHandler 事件总线 事务边界:聚合根状态持久化后,事件才被发布 保证“至少一次”投递语义

4.3 战术设计在版本迁移中的角色

聚合和领域事件直接决定了模块边界的粒度以及接口版本迁移的复杂度:

  • 聚合根作为版本锚点:每个聚合根对应一个资源端点(如 /orders/{orderId}),其版本号与聚合根的行为契约绑定。
  • 领域事件作为版本边界:事件结构是模块间异步接口的契约。事件版本升级时,需同时提供事件版本转换器(Event Upcaster)以兼容旧版本消费者。
  • 聚合内部重构不影响外部:只要聚合根的行为和事件结构不变,内部实体或值对象的调整无需升级接口版本。
关键结论:战术设计阶段的聚合划分和事件建模,直接决定了后续接口版本迁移的代价。一个边界清晰、事件版本意识强的模型,可以大幅降低版本升级的摩擦成本。