You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

领域驱动设计:如何在领域层处理外部支付网关?

符合DDD原则的支付意图架构方案

针对你遇到的三个核心困境,我们可以通过分层职责明确化、领域逻辑封装回领域层、严格保障实体创建有效性三个方向调整架构,同时完全遵循DDD原则:

1. 核心思路梳理

你的业务流程的核心约束是:

  • 内部PaymentIntent必须依赖外部网关支付意图才能处于有效状态
  • 外部网关调用前必须完成纯领域规则校验(无需依赖外部服务)

基于此,明确各层职责:

  • 领域层:负责所有纯领域逻辑(规则校验、实体状态有效性保障),保持纯净不依赖外部服务
  • 应用层:负责协调领域层、基础设施层(网关服务、仓库)的流程,不包含领域逻辑
  • 基础设施层:负责与外部系统(支付网关)、数据库的交互

2. 具体架构调整方案

(1)将领域规则校验封装回领域层

你提到的TotalPaymentSplitsEqualsIntentAmountRule和PaymentSplitReceiversMustBeUniqueRule都是纯领域规则,完全可以封装在PaymentIntent实体内部,无需依赖外部服务。

修改PaymentIntent实体,添加前置校验和严格的创建逻辑:

export class PaymentIntent {
  // 实体核心属性
  private readonly marketplaceId: string;
  private readonly customerId: string;
  private readonly amount: number;
  private readonly gatewayPaymentIntent: ExternalGatewayPaymentIntent;
  // 其他属性...

  // 前置规则校验:在创建实体前验证领域规则
  static validatePreCreation(amount: number, paymentSplitsData: PaymentSplit[]) {
    const validationErrors: string[] = [];

    // 校验拆分金额总和等于意图金额
    const totalSplits = paymentSplitsData.reduce((sum, split) => sum + split.amount, 0);
    if (totalSplits !== amount) {
      validationErrors.push("支付拆分总和必须与支付意图金额一致");
    }

    // 校验拆分接收方唯一
    const receiverIds = paymentSplitsData.map(split => split.receiverId);
    if (new Set(receiverIds).size !== receiverIds.length) {
      validationErrors.push("支付拆分接收方不能重复");
    }

    if (validationErrors.length > 0) {
      throw new DomainValidationError(validationErrors);
    }
  }

  // 严格的实体创建方法:必须传入所有必要参数,确保创建即有效
  static create(params: {
    marketplaceId: string;
    customerId: string;
    amount: number;
    paymentMethodType: string;
    gateway: string;
    businessEntityId: string;
    paymentSplitsData: PaymentSplit[];
    gatewayPaymentIntent: ExternalGatewayPaymentIntent;
  }): PaymentIntent {
    // 确保外部网关支付意图存在
    if (!params.gatewayPaymentIntent) {
      throw new Error("创建支付意图必须提供外部网关支付意图");
    }

    // 可在此添加其他实体内部状态校验
    const paymentIntent = new PaymentIntent();
    // 为实体属性赋值...
    paymentIntent.marketplaceId = params.marketplaceId;
    paymentIntent.customerId = params.customerId;
    paymentIntent.amount = params.amount;
    paymentIntent.gatewayPaymentIntent = params.gatewayPaymentIntent;
    // 其他属性赋值...

    return paymentIntent;
  }
}

// 自定义领域验证错误类
export class DomainValidationError extends Error {
  constructor(public readonly errors: string[]) {
    super("领域规则验证失败");
  }
}

(2)调整应用服务的职责,仅做流程协调

你的PaymentIntentService属于应用服务,它的职责是串联流程,不应该包含领域逻辑。修改后,应用服务只负责:

  1. 调用领域层完成前置规则校验
  2. 调用基础设施层的网关服务创建外部支付意图
  3. 调用领域层创建有效的内部PaymentIntent
  4. 持久化实体并返回结果

调整后的应用服务代码:

export class PaymentIntentService {
  constructor(
    private readonly gatewayService: GatewayService,
    private readonly paymentIntentRepository: PaymentIntentRepository
  ) {}

  async create({
    marketplaceId, gateway, customerId, amount, 
    paymentMethodType, paymentSplitsData,
  }: CreatePaymentIntentParams) {
    // 1. 调用领域层做规则校验,领域逻辑完全留在领域层
    PaymentIntent.validatePreCreation(amount, paymentSplitsData);

    // 2. 调用基础设施层的网关服务,创建外部支付意图
    const externalGatewayPaymentIntent = await this.gatewayService.createPaymentIntent({
      amount,
      paymentMethodType,
      gatewayCustomerId: customerId,
      gateway
    });

    // 3. 创建内部支付意图,确保传入所有必要参数,实体创建即有效
    const paymentIntent = PaymentIntent.create({
      marketplaceId,
      customerId,
      amount,
      paymentMethodType,
      gateway,
      businessEntityId: '',
      paymentSplitsData,
      gatewayPaymentIntent: externalGatewayPaymentIntent
    });

    // 4. 持久化实体
    await this.paymentIntentRepository.create(paymentIntent);
    return paymentIntent;
  }
}

3. 针对三个困境的解决说明

  • 向实体注入服务:所有校验规则都是纯领域逻辑,无需依赖外部网关服务,实体保持完全纯净,不依赖任何基础设施层服务
  • 始终有效的领域模型:PaymentIntent.create方法强制要求传入gatewayPaymentIntent,确保实体从创建那一刻起就处于完整、有效的状态,不存在中间无效状态
  • 领域逻辑置于服务中:所有领域规则校验都封装在PaymentIntent实体内部,应用服务仅做流程协调,完全避免了领域逻辑泄露到应用层的问题

内容的提问来源于stack exchange,提问作者Kainar Masujima

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 03:54:53