3. 支付领域通用语言(Ubiquitous Language)

在领域驱动设计中,通用语言(Ubiquitous Language)是团队(领域专家、开发、产品、测试)共享的一套精确、无歧义的术语体系。支付领域涉及资金流转、风险控制、多方对账,术语的混乱将直接导致模型错误和线上资损。本章定义支付、订单、交易、账户、商户、渠道等核心术语及其关系,作为后续聚合建模和服务划分的语义基础。

3.1 核心术语定义

下表列出支付领域最核心的 6 个术语,每个术语都附带业务定义、DDD 映射建议和典型反例(避免误用)。

术语 业务定义 DDD 映射建议 常见误用
支付(Payment) 买家为获取商品或服务,向卖家转移资金所有权的行为。包含支付指令、支付方式、支付结果。 聚合根(Aggregate Root),承载支付流程状态机 将“支付”等同于“交易”,忽略支付单的生命周期
订单(Order) 买家与商户之间达成的交易契约,记录商品/服务明细、金额、收货信息等。 独立聚合根,与支付聚合通过订单ID关联 在订单实体中直接嵌入支付状态字段
交易(Transaction) 资金在支付链路中发生的每一次原子性操作记录。例如:扣款、退款、冻结、解冻。 值对象(Value Object)或实体,取决于是否需要独立生命周期 将“交易记录”与“支付单”混为一谈
账户(Account) 资金存放的容器,包含余额、冻结金额、可用余额。可以是用户账户、商户账户、平台账户。 聚合根,账户操作必须通过领域服务保证一致性 在支付服务中直接修改账户余额
商户(Merchant) 在支付平台上注册并接受支付的商业实体。包含商户号、结算配置、费率、资质信息。 聚合根,管理商户入驻、费率变更、结算规则 将商户信息作为订单的一个属性
渠道(Channel) 实际执行资金划转的外部支付网关或银行接口。例如:支付、支付宝、银联。 聚合根或领域服务,封装渠道协议、路由、重试策略 在支付逻辑中硬编码渠道调用代码
术语边界提示: 支付(Payment)是业务行为,交易(Transaction)是资金记录。一次支付可能包含多笔交易(如:支付成功 + 退款交易)。在建模时,支付聚合根负责编排交易记录,而不是反过来。

3.2 术语关系图

下图展示 6 个核心术语之间的关联关系。箭头方向表示“依赖”或“引用”方向,数字表示多重性。

支付领域核心术语关系图 支付领域核心术语关系图 支付 (Payment) 订单 (Order) 交易 (Transaction) 账户 (Account) 商户 (Merchant) 渠道 (Channel) 引用订单ID 1:1 包含 1:N 选择渠道 更新余额 归属 1:N 结算账户 关系说明: • 支付聚合根引用订单ID(值引用,非对象嵌套),包含多笔交易记录。 • 交易记录更新账户余额,渠道负责外部网关调用。商户拥有订单和结算账户。

3.3 术语关系矩阵

为了更清晰地展示术语之间的交互关系,下表以“行→列”方向表示“依赖/引用”关系。✓ 表示存在直接关系,— 表示无直接关系。

→ 依赖方 / 被依赖方 ↓ 支付 订单 交易 账户 商户 渠道
支付 — ✓ 引用订单ID ✓ 包含交易 — — ✓ 选择渠道
订单 — — — — ✓ 归属商户 —
交易 ✓ 属于支付 — — ✓ 更新账户 — ✓ 通过渠道执行
账户 — — — — ✓ 商户结算账户 —
商户 — ✓ 拥有订单 — ✓ 拥有账户 — ✓ 配置渠道
渠道 — — — — — —
建模决策点: 支付与订单之间通过“订单ID”进行值引用,而不是对象嵌套。这保证了两个聚合根可以独立演进——订单关注商品履约,支付关注资金流转。如果直接嵌套,会导致聚合边界模糊,事务范围过大。

3.4 通用语言在代码中的体现

通用语言必须落实到代码模型中。以下是一个支付聚合根的简化代码示例,展示术语如何映射为类名、方法名和参数名。

// 支付聚合根 —— 对应通用语言中的 Payment
public class Payment : AggregateRoot<PaymentId>
{
    // 值引用订单ID,不嵌套订单对象
    public OrderId OrderId { get; private set; }
    
    // 支付方式:WECHAT_PAY, ALIPAY, UNION_PAY
    public PaymentMethod Method { get; private set; }
    
    // 支付金额(单位:分)
    public Money Amount { get; private set; }
    
    // 支付状态机:INIT → PROCESSING → SUCCESS / FAILED
    public PaymentStatus Status { get; private set; }
    
    // 交易记录集合 —— 值对象
    private List<Transaction> _transactions = new();
    public IReadOnlyList<Transaction> Transactions => _transactions;
    
    // 领域行为:发起支付
    public PaymentResult Execute(Channel channel, Account buyerAccount)
    {
        // 1. 校验状态
        // 2. 调用渠道执行扣款
        // 3. 记录交易
        // 4. 更新账户余额(通过领域事件通知)
        // 5. 更新支付状态
    }
    
    // 领域行为:退款
    public RefundResult Refund(Money refundAmount, Account buyerAccount)
    {
        // 校验退款金额不超过可退金额
        // 记录退款交易
        // 更新账户余额
    }
}
反模式警告: 不要在支付聚合根中直接引用账户对象或商户对象。账户和商户是独立的聚合根,应该通过领域事件或领域服务进行协作。直接引用会导致跨聚合的事务耦合,破坏微服务边界。

3.5 术语使用规范

为了保持通用语言的一致性,团队需要遵守以下规范:

  1. 禁止同义词混用:例如“支付单”和“支付记录”统一为“支付”;“流水”和“交易记录”统一为“交易”。
  2. 禁止跨层借用:数据库表名、API 接口名、UI 文案必须与通用语言保持一致。例如数据库表名使用 payment 而非 pay_info。
  3. 禁止属性污染:订单聚合中不应出现“支付状态”字段。支付状态是支付聚合的职责,订单只关心“是否已支付”,通过查询支付聚合获取。
  4. 术语变更流程:任何术语修改必须经过领域专家确认,并同步更新到代码、文档、测试用例和 API 文档中。

3.6 术语与限界上下文映射

下图展示核心术语在微服务架构中的限界上下文分布。每个上下文拥有自己的通用语言子集,上下文之间通过事件或API通信。

术语与限界上下文映射 术语与限界上下文映射 订单上下文 订单 (Order) 商户 (Merchant) 负责订单生命周期 商品履约、物流 支付上下文 支付 (Payment) 交易 (Transaction) 渠道 (Channel) 负责资金流转 支付状态机、路由 账户上下文 账户 (Account) 负责资金安全 余额变更、冻结 对账、结算 事件: PaymentCompleted 事件: AccountUpdated
上下文映射要点: 订单上下文通过发布 PaymentCompleted 领域事件来通知订单支付成功,而不是直接调用订单服务修改状态。账户上下文监听 AccountUpdated 事件进行余额变更。这种事件驱动方式保证了每个上下文的独立性和最终一致性。

3.7 术语一致性检查清单

在代码评审和模型评审中,使用以下清单检查通用语言的一致性:

  • ☐ 所有聚合根名称是否与通用语言一致?
  • ☐ 跨聚合引用是否使用ID而非对象?
  • ☐ 领域事件名称是否使用过去时态(如 PaymentCompleted)?
  • ☐ 数据库表名、字段名是否与通用语言映射?
  • ☐ API 路径是否使用术语(如 /payments 而非 /pay)?
  • ☐ 是否存在同义词混用(如“流水”和“交易记录”)?
  • ☐ 是否存在跨上下文的状态字段冗余?

通过以上定义和规范,团队可以在支付、订单、交易、账户、商户、渠道之间建立清晰的语义边界,为后续的聚合划分和服务通信奠定坚实的语言基础。