如果你已经看到第四篇,说明你已经不再只是"写代码",而是在真正思考系统。
前三篇我们做了三件事:
- 第一篇:搞清楚订单系统有哪些核心模型
- 第二篇:学会用"聚合"划清边界
- 第三篇:把 DDD 落地到代码结构
但还有一个绕不开的问题:
当系统变大,必须拆成微服务的时候,这些模型和聚合该怎么落?
这一篇,我们就专门聊这个:微服务拆分 + 聚合边界,到底怎么对齐。
一个常见误区:按"技术"拆服务
很多团队一开始会这么拆:
- order-service
- payment-service
- user-service
- inventory-service
看起来没问题,但实际很容易变成:
- 服务之间强依赖
- 调用链越来越长
- 改一个功能要动 3 个服务
本质问题是:
拆分依据是"表"或"技术",而不是"业务边界"。
正确的出发点:服务 = 边界上下文(Bounded Context)
在 DDD 里,一个很重要的概念是:
Bounded Context(限界上下文)
你可以简单理解为:
- 一块完整的业务语义
- 一套统一的模型
- 一组一致的规则
而一个微服务,理想情况下应该对应一个 Context。
回到订单系统,我们到底有几个 Context?
不要一上来就拆服务,先划边界。
我们可以这么看:
订单上下文(Order Context)
负责:
- 创建订单
- 修改订单
- 订单状态流转
核心模型:
- Order
- OrderItem
支付上下文(Payment Context)
负责:
- 支付流程
- 支付状态
- 对接第三方支付
库存上下文(Inventory Context)
负责:
- 扣减库存
- 释放库存
- 库存锁定
履约上下文(Fulfillment Context)
负责:
- 发货
- 物流状态
- 配送流程
到这里你会发现:
这和第二篇的"聚合划分"是对齐的。
聚合是内部边界,Context 是外部边界。
一个关键结论
一个微服务里可以有多个聚合,但一个聚合绝对不能跨服务。
拆分时最容易犯的错误
错误 1:一个聚合拆到多个服务
比如:
- Order 在 order-service
- OrderItem 在 item-service
结果:
- 强一致没了
- 调用复杂度暴涨
错误 2:全链路同步调用
下单流程:
- 调库存
- 调支付
- 调物流
问题:
- 链路长
- 容错差
- 一点失败全崩
更合理的方式:事件驱动
下单:
- 创建订单
- 发布 OrderCreated
库存:
- 扣库存 → 发布结果事件
支付:
- 创建支付 → 发布 PaymentCreated
订单:
- 监听事件更新状态
一致性怎么选?
强一致:
- 订单 + 订单项
- 金额计算
最终一致:
- 支付
- 库存
- 发货
一句话:
跨服务不要强一致。
拆服务的正确顺序
- 单体内先做好 DDD
- 模块隔离
- 再拆服务
否则就是分布式灾难。
写在最后
从单体到微服务,不是技术升级,而是边界能力升级。
如果你走到这一步,其实已经具备系统设计能力了。