3. 支付订单领域分析:识别核心子域与通用语言建立

在领域驱动设计(DDD)中,领域分析的第一步是识别核心子域、支撑子域和通用子域。对于支付订单系统,我们通过业务事件风暴和用例分析,将整个业务域拆解为四个核心子域:订单、支付、账户和库存。每个子域拥有独立的业务逻辑、数据边界和演化节奏。

3.1 核心子域识别矩阵

下表从业务价值、复杂度、变更频率三个维度对比四个子域,帮助团队建立统一的优先级认知。

子域 业务价值 业务复杂度 变更频率 DDD 分类
订单子域 核心流程入口 高(状态机、超时、拆单) 中 核心子域
支付子域 资金流转核心 极高(渠道对接、对账、幂等) 高 核心子域
账户子域 用户资产安全 高(余额、冻结、流水) 低 支撑子域
库存子域 实物/虚拟资源 中(预占、释放、超卖) 中 支撑子域
实践提示:支付子域通常需要独立部署,因为它对接多个外部支付网关,变更频率最高。订单子域作为流程编排者,应保持对支付和库存的最终一致性依赖,而非强事务。

3.2 通用语言(Ubiquitous Language)建立

通用语言是 DDD 的基石。以下是我们为支付订单系统定义的统一术语表,所有团队成员(产品、开发、测试、运维)在沟通时必须使用这些词汇。

术语 定义 所属子域
订单(Order) 用户购买行为的业务载体,包含商品明细、金额、收货信息 订单
支付单(Payment) 针对一笔订单发起的支付请求记录,与订单 1:N 关系(支持拆单支付) 支付
交易流水(Transaction) 支付网关返回的不可变资金变动记录,用于对账和审计 支付
账户(Account) 用户在平台内的资金容器,包含可用余额和冻结余额 账户
库存预占(Reservation) 下单时临时锁定库存,防止超卖,超时未支付则释放 库存
幂等键(Idempotent Key) 全局唯一标识,用于保证同一请求多次提交只生效一次 支付/订单
结算(Settlement) 支付成功后,资金从支付渠道划入平台账户的过程 账户
避坑指南:避免使用“订单支付”“支付订单”等模糊组合词。在通用语言中,“订单”和“支付单”是两个不同的聚合根,各自拥有独立的生命周期和状态机。

3.3 子域上下文边界与交互关系

下图展示了四个子域之间的核心交互流程,以及每个子域对外暴露的领域服务接口。

支付订单系统核心子域交互图 核心子域上下文边界与交互 订单子域 Order Aggregate 创建订单 · 状态机 支付子域 Payment Aggregate 发起支付 · 幂等处理 账户子域 Account Aggregate 余额管理 · 流水记录 库存子域 Inventory Aggregate 预占 · 释放 · 扣减 创建支付单 支付结果回调 资金入账 预占库存 释放/扣减结果 交互原则 • 订单与支付:异步回调 • 支付与账户:最终一致 • 订单与库存:预占+超时释放 数据边界 • 每个子域独立数据库 • 通过领域事件通信 • 禁止跨库直接查询

3.4 子域间的领域事件

子域之间通过领域事件实现松耦合通信。以下是核心领域事件清单:

事件名称 发布子域 订阅子域 触发条件
OrderCreated 订单 库存、支付 用户提交订单成功
PaymentSucceeded 支付 订单、账户 支付网关返回成功
PaymentFailed 支付 订单 支付网关返回失败或超时
InventoryReserved 库存 订单 库存预占成功
InventoryReleased 库存 订单 订单取消或超时未支付
AccountCredited 账户 支付 资金成功入账
架构决策:领域事件采用事件总线(Event Bus)异步投递,配合本地消息表保证至少一次投递。支付成功事件是系统中最关键的事件,必须支持幂等消费。

3.5 通用语言在代码中的映射

通用语言不仅停留在文档中,还要直接映射到代码的包结构和类名。以下是一个典型的 Java 项目模块划分:

com.payment.order
├── order-subdomain
│   ├── domain
│   │   ├── aggregate
│   │   │   └── Order.java
│   │   ├── valueobject
│   │   │   ├── OrderId.java
│   │   │   ├── Money.java
│   │   │   └── OrderStatus.java
│   │   └── event
│   │       └── OrderCreatedEvent.java
│   └── application
│       └── OrderService.java
├── payment-subdomain
│   ├── domain
│   │   ├── aggregate
│   │   │   └── Payment.java
│   │   ├── valueobject
│   │   │   ├── PaymentId.java
│   │   │   └── IdempotentKey.java
│   │   └── event
│   │       └── PaymentSucceededEvent.java
│   └── application
│       └── PaymentService.java
├── account-subdomain
│   ├── domain
│   │   ├── aggregate
│   │   │   └── Account.java
│   │   └── valueobject
│   │       ├── AccountId.java
│   │       └── Balance.java
│   └── application
│       └── AccountService.java
└── inventory-subdomain
    ├── domain
    │   ├── aggregate
    │   │   └── Inventory.java
    │   └── valueobject
    │       ├── SkuId.java
    │       └── Reservation.java
    └── application
        └── InventoryService.java
编码规范:每个子域的 domain 包对外部子域完全不可见,只通过 application 层的服务接口暴露能力。聚合根命名必须与通用语言完全一致,例如 Order、Payment、Account、Inventory。

3.6 子域依赖与通信模式

下图展示了子域之间的依赖方向与通信模式,强调核心子域(订单、支付)不应直接依赖支撑子域的内部实现。

子域依赖与通信模式 子域依赖方向与通信模式 应用层(Application)—— 编排领域服务 订单领域 OrderService 支付领域 PaymentService 账户领域 AccountService 库存领域 InventoryService RPC/消息 RPC RPC 通信模式说明 • 订单→支付:异步消息 • 订单→库存:同步RPC • 支付→账户:同步RPC • 所有跨域调用必须幂等
关键原则:订单子域作为流程编排者,只依赖支付和库存的防腐层(Anti-Corruption Layer)接口,不直接依赖内部实现。支付子域对账户子域的调用同样遵循此原则。这种设计保证了每个子域可以独立演进、独立部署。

3.7 本章小结

  • 四个核心子域:订单、支付、账户、库存,各自拥有独立的聚合根和数据库。
  • 通用语言:统一了 7 个核心术语的定义,确保跨团队沟通无歧义。
  • 领域事件:6 个关键事件驱动子域间的最终一致性协作。
  • 代码映射:包结构直接反映子域边界,聚合根命名与通用语言一致。
  • 通信模式:同步 RPC 用于实时性要求高的场景,异步消息用于最终一致性场景。