4. 上下文映射图:使用 Context Map 描述服务间关系
在微服务架构中,服务边界界定之后,下一个关键问题就是:这些服务之间如何协作? 上下文映射图(Context Map)是领域驱动设计(DDD)中用于描述限界上下文(Bounded Context)之间关系的核心工具。它不关注服务内部的实现细节,而是聚焦于服务之间的集成模式、数据流向和团队协作方式。
本章将深入讲解三种最常用的上下文映射模式:共享内核(Shared Kernel)、客户-供应商(Customer-Supplier) 和 防腐层(Anti-Corruption Layer),并结合工程落地场景给出具体判断标准和实施建议。
4.1 上下文映射的核心概念
上下文映射图本质上是一张服务关系拓扑图,它用标准化的符号和连线表示两个限界上下文之间的集成策略。每个限界上下文对应一个微服务或一个独立的业务模块,连线上的标签则代表双方约定的协作模式。
| 映射模式 | 协作方向 | 耦合度 | 典型场景 |
|---|---|---|---|
| 共享内核(Shared Kernel) | 双向依赖 | 高 | 通用领域模型、基础值对象 |
| 客户-供应商(Customer-Supplier) | 单向依赖(上游→下游) | 中 | 订单服务依赖库存服务 |
| 防腐层(Anti-Corruption Layer) | 单向依赖(下游保护) | 低 | 对接遗留系统或第三方服务 |
| 开放主机服务(Open Host Service) | 单向依赖(发布-订阅) | 低 | 事件驱动架构 |
| 分离方式(Separate Ways) | 无依赖 | 无 | 完全独立的业务域 |
工程建议: 在项目初期,优先选择耦合度低的模式(如防腐层、开放主机服务),避免过早引入共享内核。随着业务稳定,再逐步评估是否需要提升协作效率。
4.2 共享内核(Shared Kernel)模式
共享内核是指两个或多个限界上下文共享同一份领域模型代码或核心数据结构。这种模式适用于那些变更频率极低、通用性极强的业务概念,例如:货币、地址、用户身份标识、枚举常量等。
4.2.1 适用条件
- 共享的模型在多个服务中语义完全一致,且未来不会出现分歧。
- 团队之间沟通频繁,能够快速同步变更。
- 共享部分的测试覆盖率高,回归风险可控。
4.2.2 工程落地要点
- 将共享内核抽取为独立的基础库(Shared Library),通过包管理工具(如 Maven、npm)进行版本管理。
- 共享内核中不允许包含业务逻辑,只存放值对象、枚举、常量定义。
- 每次变更必须通知所有使用方,并执行联合回归测试。
// 共享内核示例:货币值对象(Java)
public final class Money {
private final BigDecimal amount;
private final Currency currency;
public Money(BigDecimal amount, Currency currency) {
this.amount = amount;
this.currency = currency;
}
// 只提供构造和访问方法,不包含加减乘除等业务逻辑
public BigDecimal getAmount() { return amount; }
public Currency getCurrency() { return currency; }
}
避坑提示: 共享内核是耦合度最高的模式。如果两个团队对同一概念的语义理解开始出现分歧(例如“订单状态”在支付服务中只有“已支付/未支付”,在物流服务中却有5种状态),应立即将共享内核拆分为各自独立的模型,并通过防腐层进行转换。
4.3 客户-供应商(Customer-Supplier)模式
客户-供应商模式描述了一种上游(供应商)提供数据或能力,下游(客户)消费的关系。上游服务定义接口和数据契约,下游服务按需调用。这种模式在微服务架构中最为常见,例如:订单服务(客户)调用库存服务(供应商)查询可用库存。
4.3.1 关键特征
- 单向依赖: 下游依赖上游,上游不感知下游的存在。
- 契约优先: 双方通过 API 契约(如 OpenAPI、gRPC proto 文件)约定交互格式。
- 版本兼容: 上游变更接口时,必须保证向后兼容,或提供版本化接口。
4.3.2 工程落地步骤
- 定义契约: 供应商与客户共同评审并确定 API 接口、请求/响应结构、错误码。
- 契约测试: 供应商提供契约测试(Contract Test),客户端使用 Mock 验证兼容性。
- 独立部署: 供应商和客户各自独立部署,通过服务发现或 API 网关进行调用。
- 监控告警: 对接口调用成功率、延迟、错误分布进行实时监控。
案例: 在电商系统中,支付服务(供应商)提供“创建支付单”接口。订单服务(客户)在用户提交订单后调用该接口。当支付服务需要增加“优惠券抵扣”字段时,必须保持原有字段不变,新增可选字段,避免破坏订单服务的现有逻辑。
4.4 防腐层(Anti-Corruption Layer)模式
防腐层是微服务架构中保护自身领域模型不被外部模型污染的关键模式。当需要集成遗留系统、第三方服务或语义不一致的另一个微服务时,防腐层在两者之间建立一个转换层,将外部模型翻译为内部领域模型。
4.4.1 防腐层的结构
- 适配器(Adapter): 负责与外部系统通信,处理协议转换、数据格式转换。
- 转换器(Translator): 将外部数据对象转换为内部领域对象,或反向转换。
- 门面(Facade): 对外部系统的复杂接口进行简化,提供更符合内部业务语义的方法。
4.4.2 何时必须使用防腐层
| 场景 | 说明 | 示例 |
|---|---|---|
| 集成遗留系统 | 遗留系统的数据模型混乱,字段含义不清晰 | 对接20年前的 ERP 系统 |
| 对接第三方服务 | 第三方 API 频繁变更,或返回结构不符合预期 | 对接支付网关、物流平台 |
| 跨团队协作分歧 | 两个团队对同一业务概念的模型定义不一致 | 用户服务用“userId”,订单服务用“customerId” |
// 防腐层转换器示例(TypeScript)
// 外部系统返回的订单数据
interface ExternalOrder {
order_id: string;
total_amount: number;
status_code: number;
}
// 内部领域模型
interface DomainOrder {
orderId: string;
totalAmount: number;
status: 'pending' | 'paid' | 'shipped';
}
// 防腐层转换器
class OrderTranslator {
toDomain(external: ExternalOrder): DomainOrder {
const statusMap: Record<number, DomainOrder['status']> = {
0: 'pending',
1: 'paid',
2: 'shipped'
};
return {
orderId: external.order_id,
totalAmount: external.total_amount,
status: statusMap[external.status_code] || 'pending'
};
}
}
4.5 上下文映射图综合示例
以下 SVG 图展示了一个电商系统中多个微服务之间的上下文映射关系,包含共享内核、客户-供应商和防腐层三种模式。
4.6 模式选择决策树
在实际工程中,面对两个服务之间的协作需求,可以按照以下决策树快速选择最合适的上下文映射模式:
- 两个服务是否属于同一个团队维护?
- 是 → 考虑共享内核(仅限稳定通用模型)或客户-供应商。
- 否 → 优先考虑防腐层或开放主机服务。
- 外部系统的数据模型是否与内部模型语义一致?
- 完全一致 → 客户-供应商模式。
- 不一致或混乱 → 必须使用防腐层。
- 外部系统是否频繁变更?
- 是 → 防腐层 + 契约测试,隔离变更影响。
- 否 → 客户-供应商模式,保持简单。
- 是否需要实时双向通信?
- 是 → 考虑共享内核(高风险)或事件驱动(开放主机服务)。
- 否 → 客户-供应商或防腐层即可。
核心原则: 上下文映射图不是一成不变的。随着业务演进和团队认知的深化,服务之间的关系模式也需要持续重构。建议每两个迭代(Sprint)审视一次上下文映射图,检查是否存在“共享内核膨胀”或“防腐层缺失”等坏味道。
4.7 本章小结
- 上下文映射图是描述微服务间协作关系的标准化工具,包含共享内核、客户-供应商、防腐层等模式。
- 共享内核适用于稳定、通用的基础模型,但耦合度高,需严格控制变更。
- 客户-供应商是最常见的单向依赖模式,强调契约优先和版本兼容。
- 防腐层是保护内部领域模型不被外部污染的关键屏障,在集成遗留系统和第三方服务时必不可少。
- 选择模式时应遵循低耦合优先原则,避免过早引入高耦合的共享内核。