2. 领域驱动设计基础:实体、值对象、聚合、聚合根的概念回顾
在进入支付订单系统的聚合建模之前,必须重新校准 DDD 战术设计中的四个核心概念:实体(Entity)、值对象(Value Object)strong>、聚合(Aggregate) 和 聚合根(Aggregate Root)。支付领域对数据一致性、不可变性和边界划分有极高要求,理解这些概念的精确含义是后续建模的基石。
2.1 实体(Entity)—— 拥有身份标识的对象
实体最核心的特征是 唯一标识。两个实体即使所有属性值都相同,只要标识不同,就是两个不同的对象。实体的状态可以随时间变化,但标识终身不变。
| 特征 | 说明 | 支付系统示例 |
|---|---|---|
| 唯一标识 | 每个实体拥有不可变的 ID(如 UUID、业务流水号) | 订单 ID、支付单 ID、商户 ID |
| 可变状态 | 属性可随时间变化,但标识不变 | 订单状态从“待支付”变为“已支付” |
| 相等性 | 通过标识判断相等,而非属性值 | 两个订单对象只要 orderId 相同即为同一订单 |
| 生命周期 | 从创建到归档,标识始终存在 | 支付单从创建到结算完成 |
支付系统关键实体清单: 订单(Order)、支付单(Payment)、商户(Merchant)、用户账户(Account)、退款单(Refund)。每个实体都必须有全局唯一的业务标识。
2.2 值对象(Value Object)—— 描述性、不可变、无标识
值对象描述事物的 属性或度量,没有独立标识,通过所有属性值判断相等性。一旦创建就不可变,需要修改时直接替换整个对象。值对象是聚合内降低复杂度的关键工具。
- 无标识: 两个金额对象(100.00, CNY)就是相等的,不需要 ID。
- 不可变性: 修改金额时创建新对象,而非修改原对象。
- 自包含行为: 值对象可以封装业务逻辑,如金额的加法、汇率换算。
- 可共享: 同一个值对象实例可被多个实体引用。
| 值对象 | 属性 | 支付场景 |
|---|---|---|
| Money | 金额 + 币种 | 订单金额、支付金额、退款金额 |
| Address | 省/市/区/详细地址 | 商户结算地址、用户收货地址 |
| PaymentMethod | 类型 + 卡号后四位 + 有效期 | 信用卡、支付宝、支付 |
| Period | 开始时间 + 结束时间 | 结算周期、优惠有效期 |
建模建议: 在支付系统中,优先将“金额”、“费率”、“时间区间”、“卡信息”设计为值对象。这能避免实体泛滥,同时利用不可变性防止并发修改问题。
2.3 聚合(Aggregate)—— 事务一致性的边界
聚合是一组相关对象的 一致性边界。边界内的所有对象在同一个事务中必须保持数据一致。聚合通过聚合根对外暴露操作,外部对象只能通过聚合根引用聚合内的其他对象。
- 事务边界: 一次提交只修改一个聚合,保证最终一致性。
- 内部一致性: 聚合内的实体和值对象共同满足业务不变量。
- 外部引用: 外部只能持有聚合根的 ID,不能直接引用内部对象。
- 粒度控制: 聚合不宜过大,否则影响并发性能;也不宜过小,否则失去一致性意义。
常见错误: 将整个订单系统设计为一个巨型聚合,导致每次操作锁定大量数据,严重降低吞吐量。支付系统应拆分为订单聚合、支付聚合、结算聚合等独立边界。
2.4 聚合根(Aggregate Root)—— 聚合的唯一入口
聚合根是聚合中 唯一的实体,负责维护聚合内所有对象的一致性。外部对象只能通过聚合根的方法来操作聚合内部。聚合根拥有全局唯一标识,而内部实体只有本地标识。
| 角色 | 职责 | 支付示例 |
|---|---|---|
| 全局标识 | 聚合根 ID 全局唯一,内部实体 ID 仅在聚合内唯一 | 支付单 ID(全局) vs 支付明细序号(本地) |
| 一致性守卫 | 所有修改必须通过聚合根方法,确保不变量不被破坏 | 支付单聚合根确保“支付金额 ≤ 订单剩余金额” |
| 生命周期管理 | 聚合根的创建和删除决定整个聚合的生命周期 | 支付单关闭时,其下的所有支付明细一并失效 |
| 持久化入口 | Repository 只操作聚合根,级联持久化内部对象 | PaymentRepository 保存支付单及其明细 |
2.5 概念关系全景图
下图展示了四个概念在支付订单上下文中的层级关系与交互方式:
2.6 支付系统中的聚合划分原则
基于上述概念,支付订单系统的聚合划分应遵循以下原则:
- 事务边界最小化: 每个聚合只包含必须保持强一致性的对象。例如,支付单聚合包含支付明细和支付方式,但不包含商品信息。
- 聚合根选择: 选择业务生命周期最长、对外暴露最多的实体作为聚合根。支付单(Payment)比支付明细(PaymentItem)更适合作为聚合根。
- 值对象优先: 金额、费率、时间区间等无标识概念一律设计为值对象,减少实体数量,提升不可变性带来的并发安全。
- 跨聚合通信: 使用领域事件(Domain Event)实现最终一致性,避免跨聚合的直接事务依赖。
支付系统典型聚合划分:
• 订单聚合(Order Aggregate):订单 + 订单行 + 金额值对象
• 支付单聚合(Payment Aggregate):支付单 + 支付明细 + 支付方式值对象
• 结算聚合(Settlement Aggregate):结算单 + 结算明细 + 费率值对象
• 退款聚合(Refund Aggregate):退款单 + 退款明细 + 退款原因值对象
• 订单聚合(Order Aggregate):订单 + 订单行 + 金额值对象
• 支付单聚合(Payment Aggregate):支付单 + 支付明细 + 支付方式值对象
• 结算聚合(Settlement Aggregate):结算单 + 结算明细 + 费率值对象
• 退款聚合(Refund Aggregate):退款单 + 退款明细 + 退款原因值对象