本节摘要:微服务与 DDD 的关系常被说反:不是"上了微服务就该配 DDD",而是"先用 DDD 划清上下文,微服务才有得拆"。本节给出青柚商城的拆分决策框架——依据是限界上下文与团队拓扑的合力、节奏是一次一个、红线是不共享数据库——并用一个被评审会当场否掉的"按技术层拆"方案,展示微服务失败案例库里最经典的开局。
模块化单体跑满一年,拆微服务的提议在青柚商城内部被提了三次。三次的论据各不相同:第一次是"单体部署太慢";第二次是"订单模块的代码量超过十万行";第三次是"别的公司都拆了"。三次都没过——不是因为保守癖,而是因为三条论据没有一条指向真实的疼处。拆服务是一项用运行复杂度换部署与协作独立性的交易:拆完之后,分布式事务、链路排障、版本协同的成本会立刻到账,换取的应当是明确得多的东西。把交易想清楚,答案通常不来自技术评审,而来自两个更朴素的问题:谁在等谁?什么在拖累什么?
正确的拆分单位早在 2.2 就画好了:限界上下文。上下文意味着模型自治(语言、规则、数据所有权独立),模块化意味着代码边界已经立住(3.4 的三条铁规)——把一个已经自治的模块抽成独立部署的服务,改动的是部署形态,设计一分不用变。这就是 3.4 埋的伏笔兑现的时刻。
代码体积不是依据。十万行的订单模块如果内部按聚合分得好好的,编译五分钟、部署十分钟,它对团队的伤害远小于拆成五个服务后每天要跑的联调。反例同样成立:五千行的营销模块如果天天被四个上下文催着改、发布节奏互相踩,它比十万行的订单模块更值得先拆。判断量纲是变更压力与协作摩擦,不是行数。
依据二与依据三要一起看:团队拓扑与数据所有权。拆服务本质上拆的是团队——一个服务一个团队长期所有,团队内的发布不与任何其他团队协调。所以拆之前先问:这个上下文有没有一个对应的、够人的团队?没有团队只有边界,拆出来的是无主之地。数据库是红线:每个服务独占自己的存储,别的服务只能经接口或事件取数。共享数据库的"微服务"是分布式单体——部署上分家了,故障和 schema 变更还锁在一起,复杂度付了双份,独立性一点没拿到。
评审会上最值得记录的是那份"按技术层拆"的提议:把交易上下文拆成"订单接口服务、订单逻辑服务、订单数据库服务",理由是"接口、逻辑、存储各司其职,清晰"。它的失败值得每个团队引以为鉴:
提议的拆法(错误示范): order-api-service 订单接口服务:参数校验、协议转换 order-core-service 订单逻辑服务:全部业务规则 order-db-service 订单数据库服务:封装所有 SQL 一次"改一条满减规则"的真实路径: 改 order-core-service 的规则 → 重新部署 core → api 的出入参若变,重新部署 api → db 若加字段,重新部署 db 三次部署、三份联调、三个团队的排期—— 而"改一条规则"在模块化单体里,是 domain 目录里一个文件的 diff
这个方案把 4.1 的大杂院原地搬进了分布式:层与层之间每一跳都是一次网络调用、一次序列化、一个故障点,而任何业务变更几乎必然穿三层。技术层是系统的解剖结构,业务上下文才是它的器官——按解剖结构切,每一刀都切在器官中间。 微服务拆分的第一原则由此可以写成一句话:每一刀都沿着上下文边界切,绝不穿过任何一个聚合。

决定拆了,节奏上三条经验值得照抄。一次拆一个:青柚商城的第一个候选是营销——变更压力最大、被调用方最多(2.4 的开放主机服务早已把接口协议化)。拆完它稳定运行三个月,团队积累出事件链路、契约测试的经验,才动第二个。先协议后搬迁:拆分前先把上下文间调用从进程内方法改成 2.4 的防腐层加发布语言,跑稳两周再物理分开——协议没立住就拆服务,等于把没有合同的合伙关系硬变成跨国生意。拆完做减法:抽走的模块要把它在单体里的表、任务、定时器一并带走或删除,单体里不留"过渡期兼容副本"——副本就是下一个分布式单体。
拆分完成的验收不看架构图,看三个数字:该上下文的发布是否不再需要与其他团队协调排期;它出故障时,爆炸半径是否止步于自身上下文;以及改一条本域规则,是否只需要本服务的部署。三个数字全绿,这次拆分才真正落袋。
问:拆出来的服务还要不要分四层? 答:层在服务内部照旧成立——四层是领域与技术的组织方式,与部署形态无关。实际做法通常更轻:小服务的表现层与应用层可以合并成一个入口层,但领域层与基础设施层的分界永远保留,否则服务内部马上重新长出耦合。
问:两个上下文能不能共用一个数据库实例? 答:可以共用实例,不能共用 schema,更不能跨 schema 联表。共用实例是成本权衡(小团队养不起太多库),schema 隔离是所有权红线。判断标准:任何一方的 schema 变更是否需要通知另一方——需要,就说明红线已经被踩了。
问:拆分后服务间调用超时,一致性问题怎么办? 答:这正是 6.4 的正题——超时不该用重试大法硬扛,而该按 Saga 的思路显式设计补偿。这里先给一句话版本:同步调用只用于"必须立刻知道结果"的读,一切写链路优先走事件——写链路越短越好,最好只写自己、广播事实。
问:模块化单体会不会长成大泥球? 答:会不会长成泥球,取决于 3.4 那三条铁规有没有落成机器检查,与单体还是微服务无关——微服务之间照样能长出分布式泥球,调用链绕成一团、数据互为备份。泥球的本体是边界失守,模块化只是边界失守后腐烂半径小一些的形态。真实的答案反着说:立了机器检查的单体不会泥化,没立检查的微服务会泥化得更快,因为每次越界还多付一次网络开销。
架构到此收尾。第 5 章把镜头从代码摇回会议室:模型到底在哪个现场被锻造出来——一场事件风暴工作坊的完整实录。