重构笔记:从单体到模块化

我们那个跑了四年的主服务,最终长成了一个谁都不敢碰的东西:三十七万行代码,编译一次要八分钟,改一行样式要重新部署整个站点。三周前,我们终于动手拆了。

为什么要拆

不是因为”微服务很酷”,而是三个很具体的问题:

  • 发布频率被拖死——任何一个小改动都要走全量发布,一天最多发两次
  • 故障范围不可控——一个模块内存泄漏,整个站点一起挂
  • 新人上手成本极高——光是理清模块依赖就要两周

三个坑

第一个坑:急于求成。 我们一开始想一次性拆出六个服务,结果第二周就陷入分布式事务的泥潭。后来退回去,先只拆出一个最独立的模块,跑顺了再说。

第二个坑:共享数据库。 拆服务不拆库,等于没拆。但直接拆库会让所有联表查询全部失效。我们的做法是先把跨模块的联表查询改写成接口调用,确认没有遗漏后再动数据库。

1
2
3
4
5
6
7
8
// 改造前:直接联表
const orders = await db.query(
'SELECT * FROM orders JOIN users ON orders.user_id = users.id'
);

// 改造后:接口组合
const orders = await orderService.list();
const users = await userService.batchGet(orders.map(o => o.userId));

第三个坑:没有可观测性就上线。 拆分后第一次线上问题排查花了四个小时,因为我们没有跨服务的链路追踪。补上 trace id 之后,同样的问题十分钟就能定位。

教训很朴素:先修路,再开车。可观测性、CI、回滚机制这三样没准备好,拆分只会放大混乱。

现在的样子

编译时间从八分钟降到四十秒,发布频率从一天两次变成随时可发。但坦白说,运维复杂度确实上升了——这是必须接受的代价,不是免费的午餐。


重构笔记:从单体到模块化
http://example.com/posts/refactoring-notes/
作者
博主
发布于
2026年10月8日
许可协议
如果这篇文章对你有帮助,可以请我喝杯咖啡
金额随意,备注一下昵称我会看到
微信收款码
微信
支付宝收款码
支付宝