告别盲目拆分微服务:如何用“单体优先”提升开发效率与系统稳定性

|

大家好,今天我们再来来聊聊微服务。

几年前,微服务几乎是每位架构师挂在嘴边的“最佳实践”,不管业务规模如何,不搞微服务仿佛就落伍了。我此前也写过一篇文章,讨论为何要做微服务架构:

架构演变之路:为何要搞微服务架构?

但近两年,情况正悄然发生变化。Amazon、Twitter、Shopify、Uber 等头部公司陆续开始回归单体架构,技术社区的讨论也逐渐从盲目推崇走向更为冷静的思考。

怎么大家开始又不搞微服务了?是微服务不好吗?上面那篇文章的做法过时了吗?本文我将详细说明下。

对于多数处于探索期或快速发展期的团队来说,速度、稳定性与开发效率往往比服务拆分更重要。

本文将结合多个真实案例与技术背景,系统剖析微服务的常见误区与适用边界,并给出一种更稳妥、更现实的架构路径。

1. 为什么大家对微服务趋之若鹜?

如果你向一位刚接手系统的技术负责人询问架构设计,常常在聊到业务之前,就会听到“微服务”这个词。微服务架构似乎早已成为现代系统设计的默认选项,但这种选择,并不总是基于理性的判断。

单体 VS 微服务

许多团队相信,系统一旦需要扩展,就必须采用微服务架构。这个观点听起来合理,但现实往往更为复杂。当部署一次服务需要 40 分钟,当接口兼容性频繁引发通信故障,当团队将大量时间花在排查分布式追踪 ID,而非交付业务功能时,扩展性不仅没有提升,开发效率反而显著下降。

那么,为什么微服务受到如此多团队的青睐?

1.1 技术趋势的影响力

1.1.1 微服务的盛行

很多时候,这种架构选择并非基于具体需求,而是受到行业趋势的驱动。常见的路径大致如下:

一个团队从单体架构起步,系统运行良好,但随着业务增长,逐渐遇到瓶颈。此时,他们读到一篇关于 Netflix 或 Uber 如何成功迁移到微服务的文章,开始向这种模式倾斜,甚至将 Kubernetes 架构图搬上白板,作为解决当前问题的“标准答案”。

问题在于,这类案例往往难以复制。像 Netflix 这样的公司,每个服务背后都有一个专门的团队来保障其独立运行。而对于大多数工程团队来说,可能只有一两个开发者同时负责多个模块的开发与运维。在这种资源受限的现实下,照搬大型公司架构往往会带来更多负担,而非收益。

1.1.2 微服务的反思

在这种“大厂示范效应”之下,微服务曾一度风靡,但随着越来越多“回摆单体”的真实案例被披露,大家开始意识到:拆分并不是越多越好,过度拆分反而会带来以下沉重代价。于是大家又重新思考微服务架构的利弊,我这里列举了一些公司面对微服务带来的问题所做的应对措施:

公司 回摆原因 新架构/措施
Amazon Prime Video VQA 服务基于 Step Functions+Lambda 的无服务器微服务成本高、扩展瓶颈[1] 重构为长驻容器化微服务(ECS 容器),成本↓90%,吞吐量显著提升
Twitter 停用 ~80% “微服务 bloatware”导致 SMS 2FA 短暂中断 关闭冗余微服务,精简服务列表,恢复核心功能
Uber 上千微服务过度拆分,域间治理与调用链复杂[2] 推行 Domain‑Oriented Microservice Architecture,按业务域分组中粒度服务
Shopify 过度微服务导致维护与发布节奏放慢[3] 采用模块化单体(Modular Monolith),组件化解耦,保持单一部署单元
LinkedIn 服务过度拆分引发频繁调用与高延迟[4] 引入 Domain‑Driven Services,将服务重组为中等粒度域服务

2. 微服务带来的代价

尽管微服务被视为现代架构的“标准解”,但在实际落地中,它引入的问题常常被低估。其中几个关键挑战尤为值得注意。

2.1 不易察觉的延迟成本

微服务的性能开销往往并不体现在单个服务中,而是出现在它们之间的调用链路上。以电商结账流程为例,多个服务分别承担购物车、库存、定价、税费、支付等功能,哪怕每个服务的处理时间只有十几毫秒,累加起来也轻易超过百毫秒。而这还未计入网络抖动、重试机制、TLS 握手等附加成本。

相比之下,将这些逻辑整合在一个进程内的单体架构,不仅调用延迟可忽略不计,还能简化调用链、提升稳定性。即便在高性能语言中,跨服务通信依然是明显的性能瓶颈。

以电商平台的结账流程为例,微服务架构下的各服务响应时间可能如下:

服务 延迟(毫秒)
商品服务 10
库存服务 18
优惠服务 12
税费服务 20
支付服务 45
安全校验 30
通知服务 14
总计 149ms

这还没有考虑网络延迟、重试、TLS 握手等附加因素。

如果这些逻辑统一运行在一个单体应用中,结构可能如下:

1
2
3
4
5
6
7
8
public OrderResult placeOrder(Order order) {
var itemInfo = itemService.fetch(order);
var stock = stockService.reserve(order);
var discount = discountService.apply(order);
var tax = taxService.calculate(order);
var payment = paymentService.process(order);
return new OrderResult(itemInfo, stock, discount, tax, payment);
}

逻辑相同,但所有调用都在进程内完成,避免了跨服务通信的开销,系统响应时间也因此显著降低。

微服务的网络开销

2.2 部署与运维复杂度

微服务拆分带来的不是“轻量模块”,而是运维任务的指数增长。每个服务都需要独立维护构建脚本、部署模板、监控面板、访问控制和配置管理。一旦服务数量超过十个,团队就需要投入大量精力管理一个“迷你云平台”。

这类复杂度在早期阶段尤为突出。一些初创公司甚至表示,他们维护微服务花费的时间已远超新功能开发。

相比之下,单体应用只需一次构建和部署,回滚也简单直接,能有效降低发布风险和运维负担。

2.3 组织结构的不匹配

微服务本质上是一种组织形式的反映。根据康威定律,系统架构会趋向于复制团队的沟通结构。因此,只有在拥有独立职能团队的前提下,“每个团队负责一个服务”才可能成立。

但现实中,很多团队规模有限,开发、测试、运维资源紧张,却试图运营十多个微服务。最终的结果往往是服务无人维护、接口陈旧、告警静默、技术栈混乱,架构形同失控。

只有在你的组织结构支持的前提下,微服务才是合理的选择。

康威定律(Conway’s Law):系统的架构会趋向于复制组织的沟通结构。

如果你的团队结构是这样的:

  • 1~2 名开发
  • 1 名测试
  • 1 名运维
  • 1 名产品经理

那你根本没有能力去管理 20 个服务。

所谓“每个团队负责一个服务”的理想状况,在你没有专属服务团队时就注定会崩塌

2. 4忽视上线效率

多数初创团队最需要的是一种快速响应的交付方式,而不是复杂的服务编排和异步通信系统。单体应用配合一个可靠的数据库,常常是最直接、最高效的选择。

但在盲目追求“现代架构”的趋势下,一些团队在早期就引入了 Kafka、RabbitMQ 等组件,将简单功能拆解为多个微服务,结果不仅上线周期拉长,稳定性也难以保障。

大多数早期创业公司真正需要的是:

单体系统架构

但他们常常误打误撞地搭成了这样:

微服务架构

你猜哪个架构能更快部署上线?

在实际交付中,单体架构往往更有利于快速试错和持续迭代。相比复杂架构,稳定上线才是更重要的目标。

3. 应该如何做更合理?

3.1 先从单体构建

微服务是一种实用的架构模式,但它并不适用于所有场景。即便是微服务的支持者也指出,这种架构会带来不小的“溢价”成本(Microservice Premium),主要体现在服务治理、部署运维、系统协调等方面的额外开销。对于需求尚不复杂的系统,这种成本往往超过了其带来的收益,反而可能拖慢开发进度。因此,很多团队在面对新应用时,更倾向于从单体架构入手。

这也强化了“单体优先”的架构策略:即便预期系统未来可能发展为适合微服务的规模,也应优先以单体方式启动,待实际需要明确后,再逐步拆分。这种方式在初期能有效降低复杂度,提升迭代效率,更契合多数项目从小规模起步、逐步演进的现实路径。

为什么要先从单体构建?

选择单体架构作为新应用的起点,背后有两个关键考虑。

首先,是一种典型的 YAGNI(You Aren’t Gonna Need It)思维方式。在项目早期,往往无法确定某个软件创意是否真正对用户有价值。相比投入大量精力构建一个可扩展但复杂的系统,快速验证想法更为重要。一个架构不够理想但能被用户接受的产品,至少证明了方向成立;而一个设计精巧却没人使用的系统,则是更大的资源浪费。也正因为如此,初期更应优先追求开发速度和反馈效率,避免微服务架构所带来的管理开销。

YAGNI原则

其次,微服务的有效实施依赖于对系统边界的清晰划分,也就是合理的边界上下文(Bounded Context)设计。然而,定义这些边界并非易事,即便是经验丰富的架构师,在熟悉的业务领域也难以在一开始就划分出稳定、合适的服务边界。相比之下,从单体架构入手,可以让团队在一个统一代码库中逐步识别、验证这些边界,为后续拆分成服务提供更稳妥的基础。这也为满足微服务实施前提(Microservice Prerequisites)提供了必要的准备过程。

3.2 什么时候构建微服务?

先说清楚:微服务不是坏东西,它们只是常常被用得太早

以下这些场景中,使用微服务是有充分理由的:

只有在以下场景同时成立时,采用微服务才真正值得:你的组织已经拥有多个自治团队(通常意味着团队规模已超 50 人),每个团队可以独立负责一个或多个服务;你的系统中存在职责高度分离、部署和技术栈迥异的子域,例如计费与搜索模块需要独立节奏;部分子系统对吞吐或性能具有极高需求,需要单独扩展支持;以及必须处理敏感数据或合规要求(如 PII),需要隔离日志、加密和审计边界。在这些条件不具备的情况下,从模块化单体出发,保持简单与灵活性,往往比过早引入复杂的微服务架构更加可靠

3.3 如何迁移到微服务?

“单体优先”并不意味着放弃未来的演进空间,关键在于如何为可能的迁移做好铺垫。在实践中,团队通常会采用不同策略,将单体架构逐步过渡到微服务。

一种较为稳妥的做法,是在设计单体应用时就注重内部的模块化,明确各模块的 API 边界和数据访问方式。通过有意识地控制耦合和划分责任,这类“结构良好的单体”在需要拆分时能更顺利地演进为微服务体系。虽然这种方法听起来理想,但是大量遗留系统并不会做的这么理想化,而是一堆意大利面条式的代码块,也许给他取个中文名更好,类似炒粉。

模块化系统 VS 非模块化系统

更常见的策略,是从单体应用入手,在后续演进中逐步将部分功能以微服务的形式向边缘剥离。这种方式往往形成一个“核心单体 + 周边微服务”的结构:核心系统保持相对稳定,而新功能、新需求则通过微服务来扩展,降低对主系统的干扰。

还有一种较激进但现实的方式,是将初期的单体应用视为一种“牺牲式架构”(Sacrificial Architecture),即接受它未来会被完全替换的命运。在这种策略下,单体应用的主要价值在于帮助团队快速推向市场、验证需求。一旦业务模型和服务边界更加清晰,再基于已有经验重构为更合理的微服务系统。这种方法强调的是节奏优先,而非一开始就追求“完美架构”。

3.4 不用微服务,你也能实现模块化

构建单体应用并不等于写一堆意大利面条式的耦合代码。糟糕的单体(如完全耦合、缺乏分层)一样会导致“技术债地狱”,不是说不拆服务就一劳永逸了。可以先通过领域建模限界上下文划分,再结合技术指标和团队能力逐步演进。

比如可以采用如下模块化设计

1
2
3
4
/modules
/order 订单模块
/inventory 库存模块
/product 商品模块

每个模块都可以:

  • 独立测试
  • 拥有自己的限界上下文(Bounded Context)
  • 在将来需要时再拆分成微服务

这正是 Martin Fowler 所称的 模块化单体(Modular Monolith)。Martin Fowler 提出的 Modular Monolith[5],是一种非常务实的折中方案。它允许你:

  • 保持业务逻辑隔离
  • 在早期快速开发
  • 到达一定规模后有条不紊地拆分服务

这比一开始就上 Kubernetes、Istio、Kafka 要靠谱得多。

4. 真正该问的问题不是“我们需要微服务吗?”

而是:

“我们现在最需要优化的是什么?”

如果你的团队正处于这样的阶段:

  • 产品方向尚未稳定,仍在持续调整
  • 团队规模小,正在组建核心开发力量
  • 每周都需快速迭代、持续上线新功能

那么你最需要关注的不是“未来可扩展性”,而是开发速度、系统简单性与上线效率
在系统尚未成熟之前,过早引入微服务,只会增加复杂度,拉低交付节奏。

单体架构 vs 微服务架构 对比表

应用场景 单体架构 ✔️ 微服务架构 ✔️ 说明
小型、简单应用 单体结构更直接,部署简单
团队资源有限(如早期创业团队) 小团队更适合维护一个整体系统
运维复杂度要求低 单体更易于部署与监控
系统变化频率低 不常更新的系统无需微服务拆分
快速部署与频繁迭代需求 微服务适合独立模块快速上线
高可扩展性与故障隔离需求 微服务能按服务独立扩展与容错
技术栈多样化(不同服务用不同语言) 微服务允许按模块选择最佳技术
产品或市场快速演变 确确的说微服务更适合那些已经完成领域划分、
需要大规模并行迭代、服务稳定复用、
团队足够成熟的系统在快速变化中保持灵活。

结构良好的单体系统,同样可以支撑产品在
早期实现快速演变,甚至更具效率和稳定性。

5. 总结

微服务不是问题的起点,也不是答案的终点。它是一种应对复杂系统演进的策略,但前提是你已经清晰理解了系统的复杂性本身。

太早采用微服务,就像在还没有稳定收入前提前背上房贷。表面上看似专业、前瞻,实则是在为一套尚未成熟的系统增加运维负担和组织压力。延迟控制、部署治理、接口演进、团队协作……这些“利息”一旦积累起来,足以让开发效率长期陷入低谷。

相比之下,把单体架构打磨扎实,让系统在早期具备清晰的结构和良好的可演化性,反而更能为将来的拆分打下基础。等到真正需要演进为微服务时,你也已经积累了足够的上下文和经验,能以更低的成本、做出更贴合实际的设计决策。我见过太多复杂的单体了,他们不是因为没实施微服务而导致复杂,本身上是没做好领域边界划分,没有任何业务模块化的划分,更多是技术上的分层架构,比如典型的三层架构,业务代码全部平铺在每层里面,最终导致难以维护的大型单体系统。

架构的选择从来不是“用不用微服务”的问题,而是“系统是否真的需要它”。保持理性,尊重阶段,是每一位工程决策者应有的判断力。

References


  1. Scaling up the Prime Video audio/video monitoring service and reducing costs by 90%. Retrieved from https://www.wudsn.com/productions/www/site/news/2023/2023-05-08-microservices-01.pdf ↩︎

  2. Introducing Domain-Oriented Microservice Architecture. Retrieved from https://www.uber.com/en-US/blog/microservice-architecture/ ↩︎

  3. Deconstructing the Monolith: Designing Software that Maximizes Developer Productivity. Retrieved from https://shopify.engineering/deconstructing-monolith-designing-software-maximizes-developer-productivity ↩︎

  4. Domain-Driven Services – Just the Right Size. Retrieved from https://www.architectureandgovernance.com/applications-technology/domain-driven-services-just-the-right-size/ ↩︎

  5. Monolith First. Retrieved from https://martinfowler.com/bliki/MonolithFirst.html ↩︎