4. 支付上下文边界:限界上下文在支付系统中的划分

在领域驱动设计中,限界上下文(Bounded Context)是划分系统边界的基本单元。支付系统作为典型的金融核心域,其内部包含多个职责清晰、演化速度不同的子领域。错误的边界划分会导致模型污染、团队耦合、部署僵化。本章从支付业务本质出发,给出经过生产验证的上下文划分方案。

4.1 支付系统的核心限界上下文

一个完整的支付订单聚合系统,通常包含以下 6 个限界上下文。每个上下文拥有独立的通用语言、数据库和部署单元。

限界上下文 核心职责 典型聚合根 数据存储
订单上下文 管理交易订单生命周期、状态机、金额校验 PaymentOrder MySQL(强一致)
支付执行上下文 对接外部渠道、发起扣款、处理异步回调 Payment MySQL + 本地消息表
对账上下文 拉取渠道对账单、逐笔匹配、差异处理 Reconciliation MySQL + 对象存储
退款上下文 处理退款申请、逆向资金、退款状态机 Refund MySQL(独立库)
账户上下文 管理用户余额、冻结/解冻、流水记录 Account MySQL(分库分表)
通知上下文 商户回调、站内信、Webhook 重试 Notification Redis + MySQL
实践要点:订单上下文与支付执行上下文是最容易混淆的一对边界。订单关注"这笔交易该不该付、付多少、付给谁";支付执行关注"怎么付、渠道返回什么、失败怎么重试"。两者必须分离,否则渠道变更会污染订单模型。

4.2 上下文划分的三大原则

  1. 业务变化频率分离:订单上下文变化最慢(业务规则稳定),支付执行上下文变化最快(渠道对接频繁)。将快变与慢变隔离,避免频繁发布影响核心订单逻辑。
  2. 数据一致性边界:强一致性需求(如订单金额与支付金额校验)放在同一上下文内;最终一致性需求(如对账、通知)通过领域事件跨上下文传递。
  3. 团队结构对齐:每个限界上下文对应一个独立的小团队(2-4 人),拥有从模型到数据库的完整决策权。

4.3 上下文映射与集成关系

下图展示了支付系统中 6 个限界上下文之间的协作关系。箭头方向表示依赖或事件流向,而非数据复制方向。

支付系统限界上下文映射图 支付系统限界上下文映射图 订单上下文 PaymentOrder 聚合 支付执行上下文 Payment 聚合 退款上下文 Refund 聚合 对账上下文 Reconciliation 聚合 账户上下文 Account 聚合 通知上下文 Notification 聚合 领域事件 退款指令 渠道流水 记账请求 结果通知 解冻/扣减 图例说明: 实线箭头 = 同步调用或强依赖;虚线箭头 = 异步事件或最终一致性消息。 每个上下文拥有独立数据库,跨上下文通信只通过领域事件或防腐层。
关键设计决策:订单上下文与支付执行上下文之间使用领域事件而非 RPC 调用。订单发布 PaymentOrderCreated 事件,支付执行上下文订阅后创建 Payment 聚合。这种异步解耦使得两个上下文可以独立扩缩容,且支付执行上下文的故障不会阻塞订单创建。

4.4 上下文之间的通信模式

限界上下文之间的通信必须显式设计,避免隐式共享数据库或模型。支付系统中常用的三种模式:

  • 防腐层(ACL):当订单上下文需要查询支付执行上下文的渠道信息时,通过 ACL 转换模型,防止外部模型污染订单领域。
  • 领域事件(Domain Event):支付完成、退款成功等关键状态变更,通过事件总线广播。事件结构应包含业务 ID、事件类型、时间戳和必要载荷,不携带内部聚合细节。
  • 开放主机服务(OHS):对账上下文对外暴露 ReconciliationService 接口,供运营后台调用,接口参数使用对账上下文自己的值对象。

4.5 边界划分的常见陷阱

陷阱一:共享数据库导致模型泄漏
两个上下文共用同一张 payment_order 表,订单上下文修改了表结构,导致支付执行上下文查询失败。解决方案:每个上下文拥有独立的数据库 schema,即使物理上在同一实例,逻辑上也要通过 schema 或数据库名隔离。
陷阱二:过度拆分导致分布式事务爆炸
将支付执行拆分为"渠道选择"、"扣款发起"、"回调处理"三个上下文,导致一次支付需要跨三个上下文协调。解决方案:将强相关的流程放在同一上下文内,支付执行上下文内部可以包含多个模块,但对外只暴露一个聚合根。

4.6 上下文边界在 Go 项目中的落地

在 Go 项目中,每个限界上下文对应一个独立的 module 或 package 层级。推荐的项目结构如下:

payment-system/
├── order-context/          # 订单上下文
│   ├── domain/             # 领域模型、聚合根、值对象
│   ├── application/        # 应用服务、DTO
│   ├── infrastructure/     # 数据库、事件发布
│   └── interfaces/         # API、gRPC 端点
├── payment-execution-context/  # 支付执行上下文
│   ├── domain/
│   ├── application/
│   ├── infrastructure/
│   └── interfaces/
├── reconciliation-context/     # 对账上下文
├── refund-context/             # 退款上下文
├── account-context/            # 账户上下文
├── notification-context/       # 通知上下文
└── common/                     # 共享内核(仅限值对象、基础类型)
Go 实践建议:每个上下文内部使用 internal 目录限制包导出范围,防止跨上下文直接引用领域模型。上下文之间的通信统一通过 interfaces 层暴露的 gRPC 或消息队列接口进行,禁止在 domain 层直接 import 其他上下文的包。

4.7 本章小结