2. 领域驱动设计基础:实体、值对象、聚合、聚合根的核心概念回顾
在深入支付订单的聚合建模之前,必须重新校准 DDD 战术模式中的四个核心构造块。这些概念是划分服务边界、设计幂等 Key 以及保证数据一致性的理论基石。以下从定义、识别特征到建模规则进行系统回顾。
2.1 实体(Entity):拥有唯一标识与连续生命周期的对象
实体是 DDD 中最基本的建模单元。其核心特征在于:不是由属性值定义,而是由唯一的身份标识(ID)定义。即使两个实体的所有属性都相同,只要 ID 不同,它们就是不同的对象。
| 特征 | 说明 | 支付领域示例 |
|---|---|---|
| 唯一标识 | 每个实体拥有不可变的 ID(如 UUID、自增主键) | OrderId、PaymentId、AccountId |
| 可变属性 | 属性可以随时间变化,但身份不变 | 订单状态从“待支付”变为“已支付” |
| 生命周期 | 从创建到归档,实体持续存在 | 支付记录从创建到对账完成 |
| 相等性 | 通过 ID 判断相等,而非属性 | orderA.equals(orderB) 比较 orderId |
建模提示:在支付订单中,
Order 和 Payment 都是典型的实体。不要将“订单金额”或“支付时间”这类值对象误当作实体来建模——它们没有独立的生命周期。
2.2 值对象(Value Object):描述性、不可变、无身份
值对象用于描述实体的某个属性或特征,它没有唯一标识,且通常是不可变(Immutable)的。两个值对象如果所有属性都相同,则视为同一个对象。
- 不可变性:一旦创建,属性不可修改。需要变更时,直接替换为新的值对象实例。
- 无身份:不分配 ID,完全由属性值决定相等性。
- 自包含:通常包含验证逻辑和简单的行为(如金额的加法、地址的格式化)。
| 支付领域值对象 | 属性示例 | 为什么是值对象 |
|---|---|---|
Money |
amount、currency |
金额 100 USD 就是 100 USD,没有独立身份 |
Address |
country、city、detail |
地址相同即相同,不关心是哪个“地址实例” |
PaymentMethod |
type、lastFourDigits |
描述支付方式,不跟踪其生命周期 |
TimestampRange |
start、end |
时间段本身是值,不是实体 |
常见误区:很多团队将
Money 设计为实体,导致每次金额变更都要生成新的 ID,徒增复杂度。记住:值对象优先于实体,能用值对象就不要用实体。
2.3 聚合(Aggregate):事务一致性的边界
聚合是一组相关对象的集合,它作为一个整体参与数据变更。聚合内部的所有对象必须满足事务一致性:一个事务内对聚合的任何修改,要么全部成功,要么全部回滚。
聚合的核心规则:
- 原子性:聚合内部的操作是原子的,外部只能通过聚合根访问。
- 边界性:聚合是持久化的最小单元。一次
save操作保存整个聚合。 - 最终一致性:不同聚合之间的数据同步,允许使用事件驱动实现最终一致性。
2.4 聚合根(Aggregate Root):外部访问的唯一入口
聚合根是聚合中唯一允许被外部对象引用的实体。外部对象不能直接持有聚合内部其他对象的引用,只能通过聚合根的方法来操作内部状态。
- 全局标识:聚合根拥有全局唯一的 ID,外部通过该 ID 引用整个聚合。
- 一致性守卫:所有对聚合内部状态的修改,都必须经过聚合根的方法,确保业务规则不被绕过。
- 持久化入口:Repository 只负责持久化聚合根,级联保存内部对象。
支付场景示例:在支付订单聚合中,
Order 是聚合根。外部服务(如支付网关回调)只能通过 order.pay(paymentId, money) 方法修改状态,而不能直接操作内部的 Payment 实体。这保证了“支付金额必须与订单金额一致”等业务规则不会被绕过。
2.5 实体 vs 值对象:决策矩阵
在实际建模中,区分实体和值对象是设计聚合的第一步。以下决策矩阵可以帮助快速判断:
| 判断问题 | 是 → 实体 | 否 → 值对象 |
|---|---|---|
| 这个对象需要被单独跟踪和修改吗? | 需要(如订单状态变更) | 不需要(如金额只是数值) |
| 两个对象属性相同,但算不同吗? | 算不同(如两个订单 ID 不同) | 算相同(如两个 100 USD 等价) |
| 对象有独立生命周期吗? | 有(从创建到删除) | 没有(创建即使用,用完即弃) |
| 对象需要持久化到独立表吗? | 通常需要独立表 | 通常作为字段嵌入实体表 |
2.6 聚合设计原则:小聚合 vs 大聚合
聚合的大小直接影响系统性能和一致性模型。以下是两种风格的对比:
| 维度 | 小聚合 | 大聚合 |
|---|---|---|
| 包含对象数 | 1~3 个对象 | 5 个以上对象 |
| 事务范围 | 小,并发冲突少 | 大,并发冲突多 |
| 一致性 | 强一致性(单聚合内) | 强一致性(但范围大,性能差) |
| 适用场景 | 高频写入、高并发 | 低频写入、复杂业务规则 |
| 支付领域示例 | 订单聚合只包含 Order + Money | 订单聚合包含 Order + Payment + Items + Address + Discount |
推荐实践:在支付订单建模中,优先采用小聚合策略。将订单、支付、退款拆分为独立的聚合,通过领域事件实现最终一致性。这能显著降低数据库锁冲突,提升系统吞吐量。
2.7 本章小结:核心概念速查表
| 概念 | 一句话定义 | 支付领域典型例子 |
|---|---|---|
| 实体 | 有唯一 ID,属性可变,生命周期持续 | Order、Payment、Account |
| 值对象 | 无 ID,不可变,由属性定义相等性 | Money、Address、TimestampRange |
| 聚合 | 一组相关对象的集合,事务一致性边界 | 订单聚合(Order + Money + Payment) |
| 聚合根 | 聚合的唯一入口,外部只能通过它操作内部 | Order 作为聚合根 |