04-DDD:边界切开以后怎么连:上下文映射与防腐层实战

📅 2026/7/12 22:32:03
04-DDD:边界切开以后怎么连:上下文映射与防腐层实战
一、为什么需要上下文映射切分边界是为了隔离但业务流程天然跨边界。下单这一个动作就牵涉到下单订单上下文 → 扣减库存库存上下文 → 增加积分会员上下文 → 发起支付支付上下文 → 创建配送单配送上下文如果对接方式没规划好订单上下文就会到处直接调用别人的内部对象边界形同虚设又变回大泥球。上下文映射Context Mapping就是规划哪些上下文之间有协作、以什么关系、用什么方式集成的地图。二、几种常见的上下文关系挑最实用的讲1. 客户—供应商Customer-Supplier上游供应商提供能力下游客户使用。下游的需求能影响上游的接口设计。例订单上下文客户需要库存上下文供应商提供扣减库存能力。订单方提需求库存方排期实现接口。这是最常见的健康协作关系。2. 防腐层Anti-Corruption Layer, ACL—— 最重要重点讲防腐层 在两个上下文之间加一层翻译 隔离防止对方的模型/概念渗透进来污染自己。为什么需要它订单上下文要调支付上下文。但支付上下文尤其是对接微信/支付宝这种外部系统的数据结构很复杂、字段命名很怪、还经常变。如果订单代码里直接出现WxPayUnifiedOrderResponse这种外部类那么微信接口一改订单核心代码就要跟着改。外部那套乱七八糟的概念会慢慢渗进你干净的订单模型里。防腐层的做法在订单上下文这边建一个PaymentGateway接口用订单上下文自己的语言定义实现类负责把外部模型翻译成自己的模型// 订单上下文的领域层只认识自己定义的接口和模型publicinterfacePaymentGateway{PaymentResultrequestPay(OrderIdorderId,Moneyamount);}// 订单上下文的基础设施层防腐层实现隔离外部系统的脏ComponentpublicclassWxPaymentGatewayAdapterimplementsPaymentGateway{privatefinalWxPayClientwxPayClient;// 外部 SDKOverridepublicPaymentResultrequestPay(OrderIdorderId,Moneyamount){// 把我方语言翻译成微信语言WxPayUnifiedOrderRequestreqnewWxPayUnifiedOrderRequest();req.setOutTradeNo(orderId.value());req.setTotalFee(amount.toCents());// 微信用分// 调用外部系统WxPayUnifiedOrderResponserespwxPayClient.unifiedOrder(req);// 把微信语言翻译回我方语言——外部模型到此为止不再外泄returnnewPaymentResult(resp.getReturnCode().equals(SUCCESS),resp.getPrepayId());}}好处将来从微信支付换成支付宝只改WxPaymentGatewayAdapter这一个防腐层实现订单核心代码一行不动。外部系统的复杂和善变被牢牢挡在防腐层外面。防腐层是 DDD 在真实项目里用得最多、最值钱的模式之一。凡是对接外部系统、遗留系统、第三方服务都该加防腐层。3. 开放主机服务 发布语言OHS Published Language供应商把能力做成一套标准化、公开的接口如 REST API 统一的 JSON 格式供任意下游调用不为某个特定下游定制。例库存上下文对外提供标准的 REST APIPOST /inventory/deduct订单、营销、报表等多个上下文都按这套公开协议来调。适合一个上下文要服务很多下游的场景。4. 共享内核Shared Kernel两个上下文共享一小块公共模型/代码。慎用——一旦共享两边就耦合了改动要双方协商。只适合极少数确实稳定且必须共享的概念。三、上下文之间用什么技术手段集成集成方式主要分两类核心权衡是同步实时一致 vs “异步最终一致”方式一同步调用REST / RPC订单实时调用库存接口扣减等返回结果。优点简单、实时、强一致配合事务。缺点上下文之间强耦合 强依赖。库存服务挂了下单就下不了。流程越长雪崩风险越大。适用必须实时确认的强一致场景如下单时校验库存是否充足。方式二领域事件 消息异步强烈推荐用于解耦—— 重点订单只管下单下单成功后发布一个领域事件订单已创建。库存、会员、配送等上下文各自订阅这个事件异步响应。系统内边界订阅方上下文异步边界消息基础设施系统内边界订单上下文关键路径业务提交写入消息异步消费异步消费异步消费1. 创建订单Order.place2. 登记领域事件OrderPlacedEvent3. 消息队列RocketMQ / Kafka4. 库存上下文扣库存5. 会员上下文加积分6. 配送上下文建配送单优点上下文之间彻底解耦。订单不需要知道谁关心下单这件事以后新增风控上下文也想监听下单订单代码不用改。某个下游挂了不影响下单主流程。缺点最终一致而非强一致积分可能晚几秒才加上需要处理消息可靠投递、幂等、补偿。适用可以接受短暂延迟的解耦场景加积分、发通知、建配送单。经验法则下单必须确认库存够不够 → 同步校验强一致。下单后加积分、发券、发通知、建配送单 → 领域事件异步最终一致。四、优选门店的上下文映射地图系统外边界第三方/外部系统系统内边界优选门店限界上下文同步查询客户-供应商关键路径同步校验库存防腐层 ACL请求支付异步回调支付结果发布领域事件异步订阅异步订阅异步订阅1. 订单上下文核心域2. 商品上下文OHS 商品/价格 API3. 库存上下文同步校验/预占5. 领域事件OrderPlaced / OrderPaid6. 会员上下文加积分7. 配送上下文建配送单8. 促销上下文核销优惠券4. 支付渠道微信/支付宝五、对比传统做法的弊端传统单体里集成基本不存在——所有模块直接Autowired互相注入、直接调对方的 Service 和实体。后果任意两个模块强耦合改一处牵连一片。一个大事务横跨所有模块性能差、容易死锁。想拆微服务时发现根本拆不开到处是隐式依赖。上下文映射强迫你显式地、有纪律地规划集成关系每一条跨边界的调用都是经过设计的而不是随手Autowired出来的。六、怎么验证与排查集成问题问题排查方式常见修复下单接口因为积分服务慢而变慢看调用链确认是否同步调用了非关键下游改为领域事件异步订阅支付 SDK 字段到处出现全局搜索WxPay、Alipay是否进入订单领域层收回到PaymentGatewayAdapter防腐层MQ 重复消息导致积分加两次查消费者是否按eventId做幂等表/唯一键增加事件去重表或业务唯一约束库存偶发不一致对比同步预占、异步扣减、补偿任务日志明确库存强一致边界必要时同步预占可以用这些命令做第一轮体检# 外部 SDK 类型不应该进入领域层grep-RWxPay\|Alipay\|Kafka\|RocketMQsrc/main/java/com/youxuan/store/*/domain# 订单上下文里不应直接注入一堆其他上下文 Servicegrep-Rprivate final .*Servicesrc/main/java/com/youxuan/store/order战略设计篇到此结束。我们已经会划分子域 → 区分核心域 → 切分限界上下文 → 规划上下文集成。接下来进入战术设计篇钻进单个限界上下文内部看看领域模型的代码到底怎么写。从最基础的两块积木——实体与值对象开始。