4. 上下文映射:团队间协作模式

在领域驱动设计中,上下文映射(Context Map) 是连接战略设计与战术实现的桥梁。它不关心单个限界上下文内部的实体或值对象,而是聚焦于多个限界上下文之间的协作关系与集成方式。理解并正确选择团队间的协作模式,是避免“分布式单体”和“服务调用地狱”的关键。

本章将深入剖析四种核心协作模式:合作关系(Partnership)、共享内核(Shared Kernel)、客户-供应商(Customer-Supplier) 以及 防腐层(Anti-Corruption Layer),并结合 Go 语言工程实践给出落地建议。

4.1 协作模式全景图

不同的协作模式对应不同的组织关系、技术耦合度与变更频率。下表从关键维度对四种模式进行对比:

模式 组织关系 耦合度 变更协调 适用场景
合作关系 对等团队 低-中 频繁同步 两个上下文需频繁交换数据,共同演进
共享内核 紧密协作 高 极高 核心领域模型高度重叠,需复用同一套核心
客户-供应商 上下游 中 按契约变更 上游提供能力,下游消费,有明确接口
防腐层 隔离 低 低 集成遗留系统或第三方,保护领域模型
实践提示: 在 Go 项目中,共享内核 通常对应一个独立的 internal/core 包,被多个 Bounded Context 引用;而 防腐层 则表现为 adapter 包中的接口适配器,将外部模型转换为内部领域模型。

4.2 合作关系(Partnership)

合作关系 发生在两个对等团队之间,他们需要频繁交换信息以完成一个共同的目标。双方没有明确的上下游,而是通过同步接口或事件驱动的方式保持数据一致。

  • 特征: 双向依赖、频繁通信、共同计划迭代。
  • 风险: 容易演变为“紧耦合”,一个团队的变更会阻塞另一个团队。
  • Go 实践: 使用 interface 定义双向契约,双方各自实现。推荐使用 gRPC 双向流 或 消息队列(如 NATS) 实现异步解耦。
// 合作关系下的双向接口定义示例
type OrderService interface {
    CreateOrder(ctx context.Context, order *Order) error
    OnPaymentReceived(ctx context.Context, payment *Payment) error
}

type PaymentService interface {
    ProcessPayment(ctx context.Context, payment *Payment) error
    OnOrderConfirmed(ctx context.Context, orderID string) error
}

4.3 共享内核(Shared Kernel)

共享内核 是耦合度最高的模式。多个限界上下文共享同一份领域模型的核心部分,例如 User、Money、Address 等值对象或实体。这部分代码由所有相关团队共同维护,任何修改都需要所有团队评审。

警告: 共享内核是“双刃剑”。在 Go 中,过度使用共享内核会导致 import cycle 和包依赖混乱。建议只共享 值对象 和 领域事件,避免共享包含业务逻辑的实体或聚合根。

共享内核的典型结构:

// shared/kernel/valueobject.go
package kernel

import "github.com/shopspring/decimal"

type Money struct {
    Amount   decimal.Decimal
    Currency string
}

// 不可变,只通过 NewMoney 创建
func NewMoney(amount decimal.Decimal, currency string) Money {
    return Money{Amount: amount, Currency: currency}
}

4.4 客户-供应商(Customer-Supplier)

这是最常见的上下游模式。供应商(上游) 提供能力或数据,客户(下游) 消费这些能力。双方通过明确的契约(Contract) 进行交互,供应商负责实现契约,客户依赖契约。

  • 上游(Supplier): 定义接口、发布事件、提供 SDK 或 API。
  • 下游(Customer): 调用接口、订阅事件、实现回调。
  • 变更管理: 上游变更契约时,需通知下游并给出迁移期。

在 Go 中,推荐使用 protobuf 定义契约文件,作为团队间的“合同”:

// supplier.proto (上游定义)
service InventoryService {
    rpc CheckStock(StockRequest) returns (StockResponse);
}

message StockRequest {
    string sku = 1;
    int32 quantity = 2;
}

message StockResponse {
    bool available = 1;
    int32 remaining = 2;
}

4.5 防腐层(Anti-Corruption Layer)

防腐层(ACL) 是保护领域模型不被外部系统“污染”的关键模式。当集成遗留系统、第三方 API 或不同团队维护的上下文时,ACL 负责将外部模型转换为内部领域模型,隔离外部变化。

ACL 的核心组件:

  1. 接口适配器: 将外部接口转换为内部接口。
  2. 模型转换器: 将外部 DTO 转换为内部 Entity/Value Object。
  3. 门面(Facade): 对外部系统的复杂调用进行封装,提供简洁的领域接口。
// 防腐层示例:将外部 LegacyOrder 转换为内部 Order
type LegacyOrderAdapter struct {
    client *legacy.Client
}

func (a *LegacyOrderAdapter) ToDomainOrder(ctx context.Context, legacyID string) (*domain.Order, error) {
    legacyOrder, err := a.client.GetOrder(ctx, legacyID)
    if err != nil {
        return nil, err
    }
    // 转换逻辑:将外部模型映射为内部领域模型
    return &domain.Order{
        ID:        domain.OrderID(legacyOrder.OrderNumber),
        Total:     domain.Money{Amount: legacyOrder.Amount, Currency: "CNY"},
        Status:    a.mapStatus(legacyOrder.Status),
    }, nil
}

func (a *LegacyOrderAdapter) mapStatus(legacyStatus int) domain.OrderStatus {
    // 状态映射表
    switch legacyStatus {
    case 1:
        return domain.OrderStatusPending
    case 2:
        return domain.OrderStatusConfirmed
    default:
        return domain.OrderStatusUnknown
    }
}

4.6 协作模式选择决策树

在实际项目中,如何快速选择正确的协作模式?以下决策树可以帮助团队做出判断:

协作模式选择决策树 两个上下文需要协作? 是:继续分析 否:无需集成 共享核心模型? 上下游关系? 共享内核 客户-供应商 外部/遗留系统? 防腐层 决策要点: 1. 如果两个团队可以频繁同步且模型高度重叠 → 共享内核 2. 如果存在明确的上下游,且下游需要上游能力 → 客户-供应商 3. 如果集成的是外部系统或遗留系统,且不希望外部污染领域 → 防腐层 4. 如果两个团队对等且需要频繁交换数据 → 合作关系

4.7 模式选择与 Go 工程实践

在 Go 项目中,协作模式的选择直接影响包结构、接口定义和依赖管理。以下是一些实战建议:

  • 合作关系: 使用 internal/ 包隔离实现,通过 pkg/ 暴露接口。双方各自维护自己的 adapter 实现。
  • 共享内核: 将共享代码放在独立的 shared/ 或 kernel/ 包中,只包含值对象、领域事件和基础类型。避免在共享包中包含仓储或服务实现。
  • 客户-供应商: 供应商提供 client 包(SDK),下游通过依赖注入使用。契约文件(proto)由供应商维护,下游通过 go mod 引用。
  • 防腐层: 在 adapter 包中实现 ACL,对外部调用进行封装。ACL 内部使用 interface 定义领域需要的接口,外部实现通过适配器注入。
关键原则: 无论选择哪种模式,都要确保 领域层(domain) 不依赖任何外部框架或基础设施。协作模式的实现细节(如 gRPC、消息队列、REST API)都应该放在 基础设施层(infrastructure) 或 适配器层(adapter) 中。

4.8 本章小结

上下文映射中的协作模式是 DDD 战略设计的核心决策点。正确选择模式可以降低团队间的耦合,提升系统的可演进性。在 Go 语言中,利用 interface、protobuf 和清晰的包结构,可以优雅地实现这些模式。记住:模式是工具,不是教条——根据团队的组织结构和业务需求灵活选择,才是架构师的智慧。