3. 支付订单聚合的识别:从业务需求中识别支付订单聚合的边界
在领域驱动设计中,聚合(Aggregate)是保证数据一致性的最小单元。支付订单作为电商、金融等系统的核心业务对象,其聚合边界的划定直接影响系统的正确性、性能与可扩展性。本节将从真实的业务需求出发,逐步推导支付订单聚合的边界,并给出明确的建模决策。
3.1 业务需求场景分析
我们以一个典型的在线支付流程为例,梳理核心业务需求:
- 用户提交支付请求:用户选择订单并发起支付,系统创建支付订单记录。
- 调用第三方支付网关:系统向支付宝/等网关发起支付请求,获取支付链接或二维码。
- 用户完成支付:用户在支付网关侧完成付款,网关异步通知系统支付结果。
- 处理支付回调:系统接收回调,更新支付订单状态,并通知订单系统。
- 退款处理:用户或客服发起退款,系统创建退款记录并调用网关退款接口。
- 对账与结算:每日与支付网关对账,核对支付与退款记录。
这些需求背后,隐藏着多个业务对象:支付订单、支付流水、退款记录、对账记录、支付通知。它们之间的关系决定了聚合的边界。
3.2 聚合边界识别原则
在 DDD 中,识别聚合边界遵循以下核心原则:
| 原则 | 说明 | 支付订单场景应用 |
|---|---|---|
| 一致性边界 | 聚合内所有对象必须保持最终一致性或强一致性 | 支付订单与支付流水必须强一致,不能出现支付成功但流水丢失 |
| 事务边界 | 聚合是事务的最小单位,跨聚合的事务应尽量避免 | 支付订单与退款记录可接受最终一致性,适合拆分为不同聚合 |
| 业务不变量 | 聚合必须保护其内部业务规则不被破坏 | 支付金额不能超过订单金额,支付状态转换必须合法 |
| 生命周期 | 聚合根负责管理子对象的生命周期 | 支付订单聚合根负责创建和管理支付流水 |
实践提示: 支付订单聚合的边界划定,核心在于判断“支付流水”是否必须与“支付订单”在同一个事务中更新。如果支付流水丢失会导致资金损失,则必须放在同一聚合内。
3.3 支付订单聚合的边界决策
基于上述原则,我们对支付订单相关的业务对象进行聚合边界划分:
3.4 聚合边界详细说明
根据上图,我们得出以下聚合边界决策:
核心决策: 支付订单与支付流水属于同一聚合,由 PaymentOrder 作为聚合根。退款记录、对账记录作为独立聚合,通过领域事件与支付订单聚合交互。
3.4.1 支付订单聚合(强一致性)
- 聚合根:PaymentOrder(支付订单)
- 子实体:PaymentTransaction(支付流水)—— 每次支付尝试对应一条流水
- 值对象:PayMethod(支付方式)、PayResult(支付结果)
- 业务不变量:
- 支付订单总金额 = 所有成功支付流水金额之和(不可超付)
- 支付订单状态转换:INIT → PAYING → SUCCESS / FAILED / CLOSED
- 同一支付订单不允许同时存在两条进行中的支付流水
3.4.2 退款聚合(最终一致性)
- 聚合根:RefundRecord(退款记录)
- 业务规则:退款总金额 ≤ 支付成功金额 - 已退款金额
- 通信方式:通过领域事件
PaymentRefundedEvent通知支付订单聚合更新可退金额
3.4.3 对账聚合(最终一致性)
- 聚合根:Reconciliation(对账记录)
- 业务规则:对账差异记录需人工或自动处理
- 通信方式:通过领域事件
ReconciliationCompletedEvent触发差异处理流程
3.5 聚合边界对缓存策略的影响
聚合边界的划定直接影响 Redis 缓存策略的设计:
| 聚合 | 缓存粒度 | 缓存策略 | 失效时机 |
|---|---|---|---|
| 支付订单聚合 | 按聚合根 ID 缓存整个聚合 | String / Hash 存储聚合快照 | 支付状态变更、流水新增时失效 |
| 退款聚合 | 按退款 ID 缓存 | String 存储,TTL 较短 | 退款状态变更时失效 |
| 对账聚合 | 按对账日期缓存 | Hash 存储对账汇总,TTL 较长 | 每日对账完成后写入,次日失效 |
注意: 支付订单聚合内包含支付流水,缓存时必须保证聚合内所有数据的原子性。推荐使用 Redis Hash 结构,将支付订单字段与支付流水列表一起序列化存储,避免部分更新导致的数据不一致。
3.6 聚合边界验证
最后,我们通过几个典型业务场景验证聚合边界的合理性:
3.7 本章小结
- 支付订单聚合的边界以强一致性为核心,将支付订单与支付流水放在同一聚合内。
- 退款和对账作为独立聚合,通过领域事件实现最终一致性,降低事务复杂度。
- 聚合边界的划定直接影响缓存粒度与失效策略,支付订单聚合适合按聚合根 ID 整体缓存。
- 通过业务场景验证,确保聚合边界能够保护业务不变量,并支持高并发场景下的正确性。