案例说明:以下内容已脱敏,公司、业务量级与细节做了模糊化处理。写它的目的不是展示「拆成功了」,而是记录拆的次序和判断——多数服务化项目失败,不是拆得不对,是拆得太急。
背景:不是不能跑,是「改不动了」
一个跑了 6 年的单体应用,业务还在增长,但团队已经不敢动它:一次改需求要牵动十几个模块,上线窗口越来越长,一个模块的问题会把整站拖垮。团队最痛苦的不是性能,是变更的恐惧。
这是一个典型的「改不动」问题。它和「性能扛不住」是两种病,药方完全不同——性能问题要拆热点,改不动的问题要先拆边界。
先做的一件事:模块化,而不是服务化
很多团队一上来就按「订单服务、用户服务」去拆,拆完发现服务之间到处互相调用,比单体还乱。
我们前三个月只做了一件事:在单体内部把边界画出来。按业务拆解的方法论把模块边界定清楚,接口在代码层面先隔离,数据访问也各走各的路(哪怕还是同一个库)。
判断标准很简单:模块 A 的代码里,不允许直接读模块 B 的表;要拿数据,走 B 暴露的方法。先在代码层把「不该有的耦合」消灭掉,网络层的事后头再说。
拆的次序:先拆「独立且低频」的,后拆「核心高频」的
我们的拆分顺序:
- 通知与消息模块:对外依赖多、核心链路不需要它同步参与,第一个拆出去,风险最小,还能顺便验证整套发布与监控流程。
- 数据分析与报表:读多写少、和交易链路只在写入点有接口,拆出去后可以独立扩缩容。
- 核心交易链路:最后才碰。等前两个拆完,团队已经把「发布、灰度、监控、回滚」这套动作练熟了,再拆最难的。
反向教训:把「用户服务」第一个拆是常见的冲动——因为它听着最独立。但实际上用户数据几乎被所有模块引用,拆它等于先啃最硬的骨头,大概率翻车。
过渡:双写与回滚,而不是「一次性迁移」
每个模块拆分都走同一套过渡流程:
- 接口层先代理:单体先暴露 HTTP 接口,内部调用先切到接口,观察两周。
- 数据双写 + 对账:需要迁库的模块,新旧双写,每天对账,差一条都不放行。
- 保留回滚开关:切流量用开关控制,出问题 5 分钟内切回单体。服务化最怕「没有回头路」,开关就是回头路。
结果与代价(脱敏后)
- 9 个月拆出 4 个服务,核心链路变更发布时间从「按天」降到「按小时」。
- 代价也如实说:前三个月产出看起来「什么都没做」(其实在做边界),团队一度怀疑方向;双写对账的工程量和日常消耗比预期大 30%。
一句话总结
服务化的本质不是「把代码拆开」,是先把耦合拆开。耦合还在的时候上微服务,只是把同一个问题的答案,从「改不动」变成「查不动」。
方法论参考:业务架构拆解 | 分布式系统设计:不要过度设计