1. 领域驱动设计基础:聚合根、实体、值对象的概念与区别
领域驱动设计(Domain-Driven Design,DDD)为复杂业务系统的建模提供了严谨的战术模式。在支付订单领域,正确区分聚合根(Aggregate Root)、实体(Entity)和值对象(Value Object),是构建高内聚、低耦合领域模型的前提。本节将逐一解析这三个核心概念,并结合支付订单场景说明其应用。
1.1 核心概念定义与对比
| 概念 | 定义 | 标识特征 | 可变性 | 生命周期 |
|---|---|---|---|---|
| 实体(Entity) | 具有唯一标识、且在其生命周期内状态可连续变化的对象。 | 拥有 ID(如订单号、用户ID) | 可变(状态可更新) | 独立、持续 |
| 值对象(Value Object) | 通过属性值描述某个概念,无唯一标识,不可变。 | 无 ID,靠属性值相等判断 | 不可变(替换而非修改) | 依附于实体 |
| 聚合根(Aggregate Root) | 聚合的入口实体,负责维护聚合内部所有对象的一致性边界。 | 是实体的一种,拥有全局唯一 ID | 可变(通过聚合根方法) | 管理整个聚合生命周期 |
关键区别速记:实体关心“我是谁”(ID),值对象关心“我是什么”(属性值)。聚合根是“边界守卫”,外部只能通过聚合根访问聚合内部。
1.2 在支付订单模型中的应用
以一笔典型的电商支付订单为例,我们可以将领域模型拆解如下:
- 聚合根:
PaymentOrder—— 支付订单聚合的入口。外部系统(如支付网关回调、用户取消请求)只能通过PaymentOrder的方法来修改聚合内部状态。它持有全局唯一的paymentOrderId。 - 实体:
PaymentTransaction—— 支付交易记录。每次支付尝试(如首次支付、重试、退款)都会生成一个交易记录,拥有自己的transactionId,但其生命周期完全受PaymentOrder管理。 - 值对象:
Money—— 金额对象。包含amount和currency两个属性,无 ID。当需要修改金额时,直接替换整个Money实例,而非修改其内部属性。 - 值对象:
PaymentMethod—— 支付方式。描述支付使用的渠道(如支付宝、)、卡号后四位等,无独立标识,不可变。 - 值对象:
Address—— 账单地址。由省、市、街道等属性构成,无 ID,不可变。
建模原则:在支付订单聚合中,
PaymentOrder 是唯一允许被外部引用的对象。所有对 PaymentTransaction 或 Money 的修改,都必须通过 PaymentOrder 提供的领域方法完成,从而保证聚合内部的数据一致性。
1.3 聚合边界与一致性
聚合根的核心职责是维护事务一致性边界。在支付场景中,以下规则必须在同一个事务中保证:
- 订单总金额必须等于所有已确认交易金额之和。
- 订单状态(待支付、支付中、已支付、已退款)与交易状态(成功、失败、处理中)必须逻辑一致。
- 支付方式变更时,必须重置所有未完成交易的支付渠道信息。
这些规则由 PaymentOrder 聚合根的方法(如 confirmPayment()、refund())统一实施,外部调用者无需关心内部细节。
1.4 实体 vs 值对象:支付场景中的选择依据
在建模过程中,判断一个概念应该是实体还是值对象,可以依据以下问题:
- 它需要被独立追踪吗? —— 需要则选实体。例如,每笔交易记录需要被审计追踪,因此
PaymentTransaction是实体。 - 它的属性值改变后,还是同一个对象吗? —— 如果属性改变意味着对象被替换,则是值对象。例如,金额从 100 元变为 200 元,我们不会说“同一个金额对象被修改了”,而是说“新的金额对象替换了旧的”。
- 它会被多个实体共享吗? —— 值对象通常被共享且不可变。例如,多个订单可以引用同一个
Address实例(如果地址相同)。
常见误区:不要将数据库主键 ID 作为判断实体的唯一标准。在 DDD 中,实体 ID 是领域标识,而非数据库代理键。例如,
Money 在数据库中可能有行 ID,但在领域模型中它仍然是值对象,因为其领域标识是金额和货币的组合。
1.5 聚合根的设计原则
设计 PaymentOrder 聚合根时,需遵循以下原则:
- 原子性:聚合内的所有对象必须在同一个事务中保持一致。
- 最小化:聚合应尽量小,只包含必须保持一致的属性。例如,
PaymentOrder不应包含用户个人信息(如用户名、手机号),这些属于另一个聚合。 - 通过 ID 引用外部聚合:聚合之间通过 ID 引用,而非直接持有对象引用。例如,
PaymentOrder持有userId,而非User对象。 - 最终一致性:跨聚合的更新可以使用领域事件实现最终一致性。例如,支付成功事件触发订单状态更新。
1.6 小结
在支付订单领域模型中:
- 聚合根(
PaymentOrder)是事务一致性的守卫者,外部只能通过它操作聚合内部。 - 实体(
PaymentTransaction)拥有独立标识,状态可变,生命周期由聚合根管理。 - 值对象(
Money、PaymentMethod、Address)无标识、不可变,用于描述实体的属性。
正确区分这三者,是后续进行 OpenAPI 接口参数配置与测试的基础。聚合根决定了 API 的入口和资源路径,实体和值对象则决定了请求/响应体的字段结构。