本节摘要:边界画完,上下文之间的每一次往来都需要一个显式的协作条款——上下文映射(Context Mapping)就是给每对相邻上下文选一个协作模式并写进契约的动作。本节过一遍七个模式各自的适用场景与代价,重点展开其中最重要的防御工事防腐层(Anti-Corruption Layer),并给出青柚商城交易与营销之间那份用防腐层加发布语言写成的真实契约代码。
2.2 画出的地图上有五个上下文,它们之间至少存在七条往来:交易向营销请求核销、营销向交易回传核销结果、交易把支付事实广播给财务、仓储把出库事实回传给交易……每一条往来如果只停留在"调个接口"的默契上,就会重演 2.1 的故事——默契会过期,过期没人通知。上下文映射要求把每条往来升级成显式条款,条款要回答两个问题:谁迁就谁?模型渗入多深? 七个模式就是这两个问题的七种答案组合。
| 模式 | 谁迁就谁 | 模型渗入程度 | 适用场景 | 主要代价 |
|---|---|---|---|---|
| 合作 | 谁也不迁就谁,联合排期 | 双向都深 | 同一团队拥有的相邻上下文 | 一方拖延就互相卡死 |
| 共享内核 | 双方共养一小块公共模型 | 中等,共享部分一致 | 小而稳定的公共子模型 | 协调成本随共享面膨胀 |
| 客户-供应商 | 供方照顾客户方需求 | 中等,走协商契约 | 上游愿意把下游当客户 | 供方话语权过强时名存实亡 |
| 遵奉者 | 下游全盘接受上游模型 | 深,无翻译 | 上游模型够好且不可谈 | 上游改版直接击穿下游 |
| 防腐层 | 谁也不迁就,中间立翻译层 | 浅,模型不越境 | 对接遗留系统或外部供应商 | 翻译层自身的开发维护成本 |
| 开放主机服务 | 上游提供标准化的服务集 | 浅到中,走协议 | 上游要服务众多下游 | 协议设计需要前瞻与版本治理 |
| 发布语言 | 双方共守一套公开报文语言 | 浅,只共享报文 | 与开放主机服务搭配使用 | 报文语言演进需要纪律 |
七条不是菜单上随手点的菜:前两条要求组织上同属一个所有者,中间三条是权力光谱上的三个刻度,最后两条是规模化对外服务的组合拳。青柚商城的用法可作参照——交易与仓储同属订单履约线、团队关系紧密,走合作模式;物流用的是第三方承运平台,走防腐层;营销作为被多个域调用的规则供给方,走开放主机服务加发布语言。模式选择反映的是组织现实,不是架构师的审美。

七个模式里,防腐层值得单独一章的待遇——它是唯一以"防御"为核心目的的模式,也是与遗留系统、第三方打交道时的默认答案。它的定义只有一句话:在两个上下文之间立一层翻译,保证对方的模型只在翻译层出现一次,绝不渗入自己的领域模型。
为什么值得单独立一层?看反面。青柚商城早期对接第三方承运平台时,图省事直接把平台回传的 waybillStatus 字段一路透传进了交易模型,三个月后问题发作:交易代码里有十七处判断 if waybillStatus == "SIGNED",平台某天把枚举值改成 DELIVERED_CONFIRMED,十七处判断同时失效,而且没人说得清哪几处影响资金逻辑。防腐层的作用就是把这个爆炸半径锁死在一个类里。
// 防腐层:把第三方物流的模型翻译成交易上下文自己的语言 public class LogisticsAntiCorruptionLayer { private final LogisticsClient client; // 第三方平台的协议客户端 // 对内只暴露交易上下文的语言:Shipment 是交易侧自己的值对象 public Shipment fetchShipment(TradeOrderId orderId) { PlatformWaybill raw = client.queryWaybill(orderId.toExternal()); return translate(raw); } private Shipment translate(PlatformWaybill raw) { // 第三方的七种状态,收敛为交易侧关心的三态 switch (raw.getStatus()) { case "PICKUP_SCAN": case "IN_TRANSIT": case "ARRIVED_CITY": return Shipment.inTransit(raw.getWaybillNo(), raw.getScanTime()); case "SIGNED": case "DELIVERED_CONFIRMED": // 平台改枚举,只改这一行 return Shipment.signed(raw.getWaybillNo(), raw.getSignTime()); default: // 未知值不允许静默通过:记告警并按在途处理,绝不猜 auditLog.unknownStatus(raw.getWaybillNo(), raw.getStatus()); return Shipment.inTransit(raw.getWaybillNo(), raw.getLastScanTime()); } } }
这段代码体现了防腐层的四条纪律。第一,翻译层只此一家:PlatformWaybill 这个类型不允许出现在防腐层之外的任何文件里,用包可见性或独立模块强制。第二,对外输出自己的语言:交易侧拿到的是 Shipment,不是平台的报文对象。第三,未知值显式处理:第三方加新枚举是常态,防御的要点是让未知值走告警而不是走默认分支静默通过。第四,改版爆炸半径 = 一行:平台改枚举值,翻译器里一行搞定,十七处业务判断毫发无伤。
防腐层不是免费的:多一层就多一层维护,翻译逻辑本身也会出错。所以它有明确的适用线——跨组织边界(第三方供应商)默认上防腐层;组织内的核心域之间,若上游模型质量差且不可协商,也上;上游模型好、双方又同属一个团队,用合作模式更划算。判断标准始终是:模型渗入后未来五年的翻译成本,比现在建层的成本谁高。
营销上下文被交易、积分、客服三个域调用后,青柚商城把"一一迁就每个调用方"的策略换成了开放主机服务:营销把规则能力封装成一整套协议化的服务,配一份发布语言——公开报文规范,比如"优惠核销请求"的报文里,券、门槛、叠加方式各用什么字段表达、精度如何、错误码有哪些。此后新增调用方,营销不再为任何人定制;协议要演进时走版本化:v1 报文保留十八个月,新调用方一律接 v2。开放主机服务管"服务怎么开",发布语言管"报文怎么说",两者搭配,上游才不会沦为所有调用方的奴隶。
战略设计到这里交齐了三样产出:上下文地图、子域投入清单、边界协作条约。第 3 章进入战术设计——在每一块地皮上,把模型砌进代码。