单体 vs 微服务:2026 年如何选择你的架构
架构的钟摆已经回转。微服务不再是默认答案。本文介绍如何决策——包含模块化单体、抽取策略的实际建议,以及能打破争论的一个关键问题。
十年来,软件架构的传统智慧很简单:微服务是未来,单体是遗产。如果你在启动一个新项目,你会构建微服务。如果你有一个现有单体,你会计划将其拆分。问题不在于是否采用微服务,而在于你能多快到达那一步。
那个共识已经死了。过去三年产生了大量来自团队的事后分析报告,他们过早、过激进或出于错误的原因采用了微服务。Amazon 的 Prime Video 团队发表了一个案例研究,显示从无服务器微服务迁移到单体后成本降低了 90%。InnoGames 报告称通过将微服务整合回单体,基础设施复杂度减少了一半。这些故事不是异常现象——它们是一次修正的先锋。
本文不是要为一种架构优于另一种架构辩护。它是一个在它们之间做出选择的决策框架,为 2026 年而写,并受益于观察了一个完整的炒作和幻灭周期。到最后,你应该确切知道在选择下一个架构之前需要问什么问题。
钟摆已经回转
微服务最初的承诺极具诱惑力:独立部署能力、团队自主权、多语言技术栈以及水平可扩展性。每个服务可以由一个小团队开发、测试和部署,无需与任何人协调。如果一个服务失败了,它不会拖垮整个系统。如果一个服务需要扩容,你只扩容那一个服务。
这些好处是真实存在的,但它们有一个在炒作年代被系统性地低估的代价。每个微服务都会引入网络延迟、分布式系统复杂性、数据一致性挑战和运维开销。一个单体有一个部署管道、一个需要监控的应用、一个需要管理的数据库和一个需要导航的代码库。十个微服务则有十个一切,再乘以它们之间的集成点数量。
业界重新发现的基本洞见是,微服务是一种成本,而不是一种收益。它们是管理特定约束——团队规模、扩展需求、部署频率——的工具,而不是每个系统都应该追求的目标状态。如果你没有微服务要解决的问题,它们并不会让你的系统变得更好。它们只会让它更昂贵、更难变更。
微服务是针对特定问题集的一个好的解决方案。如果你没有那些问题,你就在支付微服务的成本而没有得到收益。最昂贵的架构是解决你没有的问题的架构。
2026 年的格局反映了这种修正。纯粹的微服务架构现在集中在真正需要它们的组织中——拥有数十个团队的大型工程组织、需要不同组件独立扩展的平台,以及不同服务有根本不同可靠性或延迟要求的产品。在其他地方,团队选择更简单的架构,并将微服务保留给需要它们的特定情况。
单体何时胜出
对于大多数项目来说,单体是正确的默认选择。这在经验丰富的架构师中并不是有争议的说法,但它与许多开发者在微服务炒作周期中吸收的信息相矛盾。单体在更多的场景中胜出,关键是知道哪些是那些场景。
团队规模是架构成功的最强预测因素。如果你的团队少于十名开发者,单体几乎肯定是正确的选择。在小型团队中,微服务的协调开销——对齐服务边界、管理服务间契约、维护多个部署管道——会消耗你可用工程能力的重要部分。你创建的每个服务边界都是一个需要维护的契约,而契约维护是不交付功能的工作。
创业阶段是另一个明确的信号。如果你的产品成立不到两年,或者你的商业模式仍在演进中,单体保留了快速改变方向的能力。微服务锁定了你可能在第一年就搞错的领域边界假设。单体让你可以自由地重构。当你的整个应用是一个代码库时,将一个功能从一个模块移动到另一个模块是一个重构操作。当你的应用是十个服务时,移动一个功能需要更改服务接口、更新消费者、协调部署和迁移数据。
# What a simple monolith deployment looks like in 2026
# A single Dockerfile, a single service, zero orchestration
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/server.js"]
# One docker-compose.yml for the entire stack
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://db:5432/app
db:
image: postgres:17
volumes:
- pgdata:/var/lib/postgresql/data这种设置的运维简便性怎么强调都不过分。一个需要监控的服务、一组需要检查的日志、一个需要回滚的部署。初级开发者可以在一个下午理解整个部署管道。当出问题时,只有一个地方需要查找原因。这种简便性不是奢侈品——它是随时间复利的战略优势。
领域复杂性是另一个促使走向单体的因素。矛盾的是,你的领域越复杂,过早进行服务拆分就越危险。如果你在理解其自然边界之前将一个复杂领域拆分为服务,你会创造出以各种错误方式紧密耦合的服务——无法独立部署因为更改一个就需要更改另一个的服务、共享数据库因为数据不能干净分割的服务、需要同步部署因为它们的契约不断变更的服务。
模块化单体:大多数团队从未考虑过的架构
在单体和微服务之间的非此即彼是一个虚假的二分法。模块化单体占据中间地带,对于更多团队来说比任何一个极端都更适合。模块化单体是一个单一的部署单元,具有清晰定义的内部模块,遵循与微服务相同的边界规则,但没有网络。
模块化单体与典型单体之间的关键区别在于纪律。在典型单体中,模块未强制执行——任何代码可以导入任何其他代码,随着时间的推移边界会侵蚀成一团泥球。在模块化单体中,模块有明确的公共 API 和私有实现。模块 A 只能通过 B 定义的接口与模块 B 交互。禁止跨模块边界的直接数据库访问。管理微服务通信的相同规则也适用,但通信是通过函数调用而不是 HTTP 请求进行的。
这种方法为你提供了微服务的大部分好处——强制边界、模块内的独立开发、清晰的契约——而没有运维成本。你得到一个部署管道、一个需要监控的应用、一个需要导航的代码库。但你也得到了防止泥球问题的模块边界,并使未来抽取为微服务变得简单直接。
// A modular monolith boundary in TypeScript
// Each module exposes only its public API
// modules/orders/public-api.ts
export {
createOrder,
getOrderById,
getOrdersByUser,
OrderService,
} from "./order-service";
// modules/orders/internal/ ← everything here is private
// order-repository.ts
// order-validator.ts
// order-pricing.ts
// modules/payments/public-api.ts
export {
processPayment,
getPaymentStatus,
refundPayment,
} from "./payment-service";
// Cross-module dependency is explicit and auditable
// payments/payment-service.ts imports from orders/public-api
import { getOrderById } from "../../orders/public-api";模块化单体也是对不确定未来的最佳对冲。如果你构建了一个模块化单体,后来发现某个模块需要成为一个独立服务,抽取过程是机械性的:你将模块的代码复制到一个新服务中,通过 HTTP 或消息队列暴露其公共 API,然后连接调用方。模块边界已经存在。接口已经定义。困难的工作——理解领域边界——已经完成。
如果你构建了一个没有模块边界的传统单体,后来想要抽取服务,你面临一个困难得多的问题。你必须首先发现边界应该在哪里,然后重构代码以尊重它们,然后才能抽取。这就是为什么大多数从单体到微服务的迁移都会失败——团队低估了边界发现工作,最终得到的微服务以违背目的的方式耦合在一起。
- 模块化单体:单一可部署单元,网络调用替换为函数调用,强制模块边界,方便抽取路径。
- 传统单体:单一可部署单元,无强制边界,早期阶段最大自由度,痛苦的抽取路径。
- 微服务:多个可部署单元,网络调用进行通信,强制服务边界,高运维成本。
- 模块化单体通常是最好的起点,因为它保留了选项,而不需要承诺分布式系统的复杂性。
微服务何时真正有意义
微服务没有错。它们对大多数团队来说是错误的,但有些场景下成本被收益所证明是合理的。关键是要诚实地评估你的场景是否真的符合。
独立扩展是微服务最站得住脚的理由。如果你系统的不同部分有显著不同的扩展特征——你的 API 网关需要处理每秒 100,000 个请求,而你的报告服务每秒处理 100 个请求——将它们放在同一个部署单元中会浪费资源。报告服务的硬件闲置,API 网关的自动扩展受到报告服务冷启动时间的约束。独立服务可以独立扩展,通过高效资源利用节省的成本可以抵消运维开销。
团队自主权是第二个合理的理由。当你有多个团队在同一个系统上工作,并且每个团队需要按自己的节奏独立部署时,微服务消除了协调瓶颈。团队 A 可以每天部署其服务三次,而无需等待团队 B 完成其审阅。但请注意阈值:这个论点只有在你有多个团队时才适用。如果你的组织总共只有十名开发者,你并没有一个微服务能解决的协调问题。你有一个共享 Slack 频道就能解决的沟通问题。
不同的可靠性或延迟要求也是微服务的理由。如果你的支付处理服务需要 99.999% 的可用性,而你的分析服务可以容忍偶尔的停机,将它们分开可以确保分析报告中的 bug 不会阻止客户完成结账。类似地,如果你系统的一部分需要极低的延迟而另一部分可以容忍较高的延迟,分开它们可以让你独立优化每个部分。
技术多样性是微服务最弱的理由。是的,微服务允许你为不同的服务使用不同的语言和数据库。但在实践中,大多数组织无论如何都会收敛到一小套技术上,而维护多个运行时的运维成本通常超过收益。如果你的整个团队都知道 TypeScript 和 PostgreSQL,只是为了使用不同的技术而将一个服务构建在 Rust 中、另一个在 Go 中,这是大多数组织负担不起的奢侈。
以单体起步、按需抽取服务的模式
2026 年构建软件系统最可靠的模式也是最简单的:从模块化单体开始,然后在你需要它们的证据出现时再抽取服务。这有时被称为单体优先或抽取微服务模式,并且已经成为那些经历过微服务炒作周期的组织的默认推荐。
这个模式分四个阶段运行。第一阶段是模块化单体。你将整个应用构建为具有严格模块边界的单一可部署单元。每个模块拥有自己的数据,暴露一个公共 API,保持其实现的私有性。你使用与微服务相同的纪律——清晰的契约、分离的数据所有权、显式的依赖关系——但一切都在单个进程中运行。
第二阶段是度量。你监控哪些模块变化最频繁、哪些团队在哪些模块上工作、哪些模块有不同的扩展或可靠性要求。你不是基于直觉或猜测来抽取服务。你是基于数据——真实证据表明单体正在创造一个服务边界能够解决的瓶颈。
第三阶段是抽取。你拿一个已被证明需要成为一个服务的模块——因为其变更频率导致单体部署过于频繁,或者因为其扩展要求与系统其余部分不同——并将其抽取出来。由于模块已经有清晰的边界,抽取是机械性的。你创建一个有自己的部署管道的新服务,通过 HTTP 或消息队列暴露模块的公共 API,然后更新单体以调用新服务而不是直接调用模块。
// Step 1: Define the extraction candidate as a module
// monolith/src/modules/reports/public-api.ts
export async function generateReport(
reportId: string
): Promise<ReportResult> {
// Implementation detail: queries a separate read-replica,
// takes 30 seconds, should not block the main application
}
// Step 2: When evidence shows this needs to be a service:
// 1. Create a new service from the module code
// 2. Expose the same API over HTTP
// 3. Replace the direct call with a service client
// monolith/src/clients/reporting-service.ts
const client = new ServiceClient({
name: "reporting",
baseUrl: process.env.REPORTING_SERVICE_URL,
timeout: 60000, // This service is slow
});
export async function generateReport(reportId: string) {
return client.post("/reports", { reportId });
}
// The monolith does not need to redeploy — the client handles
// retries, timeouts, and circuit breaking internally.第四阶段是重复。随着系统增长,你重复这个循环——度量、抽取、再次度量。一些你抽取的服务可能需要进一步拆分为更小的服务。一些可能需要被合并回单体,如果抽取没有带来价值的话。关键是每次抽取都是由证据驱动的,而不是由架构教条驱动的。
这种模式相对于微服务优先方法有一个关键优势:它推迟了不可逆的决策。你创建的每个服务边界都是一个对分布式系统复杂性的不可逆承诺。一旦服务存在,你就不能轻易地更改其边界而不破坏客户端。通过从单体开始仅在需要时抽取,你确保每个服务边界都是由实际需求而不是对未来需求的猜测来证明的。
如何选择:一个决策框架
当你设计新系统或评估当前架构时,按顺序思考这些问题。答案会引导你走向正确的架构,而无需你预测未来。
第一个问题:有多少开发者在这个系统上工作?如果答案少于十人,从单体开始——最好是模块化的。你没有微服务能解决的协调问题,你也负担不起运维开销。如果答案多于十人,答案取决于他们如何组织。如果他们作为一个单一团队工作,单体仍然可行。如果他们被组织成多个自主团队,微服务可能值得考虑。
第二个问题:你的系统是否有具有根本不同扩展特征的部分?如果你系统的每个部分都需要以大致相同的速率扩展,分开它们没有好处。如果一个部分需要处理每秒一万个请求而另一个处理十个,分开它们——但先从只将高扩展部分分离到自己的服务开始,而不是整个系统。
第三个问题:你能按相同的时间表部署系统的所有部分吗?如果可以,单体简化了你的部署管道并降低了协调成本。如果不能——因为系统的不同部分有不同的发布周期、监管要求或风险特征——微服务让每个部分遵循自己的部署节奏。
第四个问题:如果系统所有部分同时宕机会发生什么?如果答案是业务完全停止,你并没有从微服务中获得韧性——你只是在支付成本。真正的韧性不仅需要独立服务,还需要独立基础设施、独立数据存储和服务的优雅降级。大多数团队没有构建这些。他们构建的是在部署上紧密耦合、在理论上松散耦合的服务,这是最坏的情况。
最好的架构是你的团队能够自信部署、快速调试和无惧变更的架构。无论在你的特定组织中是什么样的——单体、模块化单体还是微服务——那就是正确的答案。其他一切都是披着工程原则外衣的架构时尚。
第五个问题:你对领域边界有多确定?如果你在构建一个充分理解的领域,使用成熟的模式——电子商务、内容管理、计费——你的领域边界相对稳定,微服务的风险较低。如果你在构建一个新领域,边界仍在涌现中,单体保留了随着你的理解演进而重构的能力。过早的服务边界会成为约束,拖慢你恰好需要进行的那个学习过程。
2026 年对大多数团队来说诚实的答案是模块化单体。它给了你微服务的纪律而没有运维成本。它保留了以后抽取服务的选择,而不强迫你今天承诺分布式系统的复杂性。它可由单个开发者部署、通过单个日志流调试、通过单个拉取请求变更。如果你的系统增长到单体不再有效,你构建的模块化边界将使向微服务的过渡比你预期得更顺畅。
架构的钟摆已经摆回了简单。那不是倒退。这是业界从经验中学习。忽视炒作或迅速从中恢复的团队是那些在交付功能而不是迁移服务的团队。选择让你能够交付的架构,并且只有在你的系统告诉它需要改变时才改变它。
