摘要
Modular Monolith 是一种“保持单一部署单元,但在内部强制清晰模块边界”的架构。对于独立开发者、小团队,以及越来越多和 AI 一起开发产品的人来说,它往往比过早引入 Microservices/MSA 更现实。
从经营公司、也亲自写代码的角度看,我最近明显感到:问题已经不再只是“以后会不会变大,所以要不要一开始就 MSA”,而是“能不能让 AI 和人都看得懂边界,同时保持部署简单”。这篇文章会比较 Monolith、Modular Monolith 和 Microservices/MSA,并整理实际选择标准。
目录
本文内容
背景
过去一段时间,Microservices/MSA 经常被当成大型系统的标准答案。按业务能力拆分服务、独立部署、隔离故障、让团队独立推进。这些优点在合适规模下确实存在。
但小团队很快会感受到成本。哪怕只有五个服务,也会多出认证、网络、部署流水线、日志、Tracing、故障点和数据一致性问题。很多时候,真正拖慢速度的不是业务代码,而是服务之间的连接和运维。
Modular Monolith 不是回到没有结构的旧式 Monolith。它更像是:像设计 MSA 一样认真对待 domain boundary,但在业务真正需要分布式之前,保持部署和运维简单。
三种架构的基本区别
传统 Monolith
Monolith 通常是一个代码库、一个部署单元、一个运行时。启动很快,本地调试简单,数据库事务也直接。
问题出现在边界消失之后。Order 代码直接改 Member 表,Payment 代码知道 Shipping 的内部实现,最后变成“大家都不敢改”的大项目。
Modular Monolith
Modular Monolith 仍然是一个应用部署,但内部按 domain module 分开。比如 member、order、payment、notification,每个模块拥有自己的规则,并只暴露有意设计的入口。
src/
member/
order/
payment/
notification/
shared-kernel/ # 尽量小
这样才是正常状态:运行时保持简单,但代码理解、测试和未来拆分都有边界。
MSA / Microservices
Microservices 会把系统拆成多个可独立部署的服务,通常按业务能力划分。每个服务倾向于拥有自己的数据,通过 HTTP、gRPC、Message/Event 进行通信。
它的优势是独立扩展、独立部署和更好的故障隔离。代价是分布式系统复杂度:timeout、retry、idempotency、observability、eventual consistency 和部署自动化。
Monolith vs Modular Monolith vs MSA 对比
| 标准 | Monolith | Modular Monolith | MSA / Microservices |
|---|---|---|---|
| 部署单元 | 一个 | 一个 | 多个服务 |
| 内部边界 | 容易变弱 | 通过 domain module 强制 | 通过 service boundary 强制 |
| 初期开发速度 | 快 | 快,但需要规则 | 容易变慢 |
| 运维复杂度 | 低 | 低到中等 | 高 |
| 数据一致性 | 本地事务简单 | 所有权清晰时仍然可控 | 需要处理分布式事务或最终一致性 |
| 故障隔离 | 弱 | 代码隔离强,进程隔离弱 | 更强 |
| 团队适配 | 小团队 | 独立开发者、小团队、复杂 domain | 按 domain 拆分的团队 |
| 与 AI 协作 | 项目变大后 context 困难 | 适合按 module 给 context | 服务 context 小,但运维知识更多 |
| 未来扩展 | 无边界时拆分成本高 | 边界真实时更容易抽取服务 | 已经分布式,但持续支付分布式成本 |
为什么适合独立开发者 + AI
AI 更需要清晰的 bounded context
如果项目内部依赖混乱,prompt 会越来越长,修改范围也更危险。有了真实 module boundary,就可以明确告诉 AI:“只修改 order module;payment 只能通过 public interface 调用,不要改内部实现。”
单一部署能保护开发速度
独立开发者每多一个服务,就多一个要部署、监控、加密、排障的对象。AI 写代码很快,但生产环境仍然只有一个人负责。很多时候,单一部署单元反而是更好的速度保护。
测试仍然可以模块化
不拆服务也能按边界测试。最基本的层次可以是:
module unit test → module integration test → application smoke test
这样 AI 生成的修改也有安全网,而不必过早进入分布式运行时。
实际设计时要看的标准
先划分 domain boundary
不要先追求漂亮的目录。先问:哪部分业务因为什么原因变化?Member、Order、Payment、Settlement、Notification 经常因为不同原因变化,所以是常见 module 候选。
规定 module 访问规则
最常见的失败是目录分开了,但仍然随意 import 其他 module 的内部 repository。更好的方式是只开放 application service、facade 或 event。
一个数据库可以,但所有权不能模糊
Modular Monolith 可以从一个数据库开始。关键是 table ownership。Order module 如果随意 update Payment 表,未来拆服务会非常痛苦。
shared code 要小
巨大的 common package 会变成隐藏的 Monolith。共享代码只放稳定类型和工具函数,业务规则留在拥有它的 domain module。
开发与运维上的差异
Observability 要提前设计
即使是单一应用,日志也应该带 module name、request id 和 domain id。
module=order request_id=... order_id=... event=OrderCreated
这个习惯会让未来抽取服务更容易。
单一部署不等于大版本发布
可以用小 PR、feature flag、module changelog 和 smoke test。一个部署产物也可以小步安全发布。
拆服务前先测量
不要因为“以后可能要单独扩展”就先拆服务。先确认瓶颈是否真实,cache 或 query tuning 能否解决,独立部署是否真的值得。
从 Modular Monolith 迁移到 Microservices
健康策略不是“为了未来先 MSA”,而是“现在建立边界,等有真实理由时再抽取”。
1. 在代码中强制边界
可以使用 package dependency check、architecture test,或 Spring Modulith 这类工具,让规则进入 CI,而不是靠团队记忆。
2. 整理数据所有权
准备抽取的 module 应该拥有自己的表。其他 module 应该通过 public API 或 event 交互,而不是直接写表。
3. 先使用内部事件
第一天不需要 Kafka。可以先在内部表达 “OrderCreated”、“PaymentCompleted” 这样的事实。以后转到外部消息系统会更自然。
4. 只抽取有强理由的 module
独立扩展、独立部署、故障隔离、独立团队所有权,这些才是强理由。没有这些理由,留在 Modular Monolith 里通常更好。
常见误区与失败场景
“只分目录就是 Modular Monolith 吗?”
不一定。判断标准是依赖方向,不是目录名称。如果每个 module 都能随意调用别人的内部 repository,边界就不存在。
“MSA 一定更现代吗?”
Microservices 很强,但不是免费的。Service discovery、gateway、monitoring、tracing、deployment automation 和 data consistency 都会变成产品工作的一部分。
“一个数据库会阻止未来拆分吗?”
不一定。更危险的是没有所有权。一个数据库但 table ownership 清楚,往往比多个服务共享同一批表更健康。
“AI 会让架构不那么重要吗?”
不会,反而更重要。AI 在清晰边界内很快;没有边界时,它也可能快速做出大范围、高风险修改。
什么情况下选哪一种
适合传统 Monolith 的情况
Prototype、短期内部工具、简单 CRUD 系统,可以直接 Monolith。只要产品可能长期存在,建议早期就加一点 module boundary。
适合 Modular Monolith 的情况
独立开发者、小型 startup team、AI-assisted product、domain 已经变复杂但组织还没有按 service 拆分时,很适合 Modular Monolith。这也是我现在对很多新项目更倾向的默认值。
适合 MSA 的情况
团队已经按 domain 分开,确实需要独立部署,有明确扩展或故障隔离需求,并且具备分布式系统运维能力时,MSA 才更合适。
结论
我的实际结论是:对于独立开发者 + AI,以及很多小团队来说,Modular Monolith 是更现实的默认选择。 它保持部署简单,同时让人和 AI 都能理解系统边界。
但这并不是说永远不要 Microservices。当组织、流量、部署节奏和故障隔离需求足以抵消成本时,MSA 当然可以是正确选择。关键不是一开始有多少服务,而是一开始有没有清晰边界。
你怎么看?在 AI-assisted development 时代,独立开发者和小团队的默认架构应该是 Modular Monolith 吗?
参考资料
- Microsoft Azure Architecture Center – Microservices architecture style: https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/microservices
- Microsoft Azure Architecture Center – Use domain analysis to model microservices: https://learn.microsoft.com/en-us/azure/architecture/microservices/model/domain-analysis
- AWS Prescriptive Guidance – Decomposing monoliths into microservices: https://docs.aws.amazon.com/prescriptive-guidance/latest/modernization-decomposing-monoliths/welcome.html
- Martin Fowler – Microservices: https://martinfowler.com/articles/microservices.html
- Martin Fowler – Monolith First: https://martinfowler.com/bliki/MonolithFirst.html
- Spring Modulith Reference Documentation: https://docs.spring.io/spring-modulith/reference/
- Thoughtworks – When (modular) monolith is the better way to build software: https://www.thoughtworks.com/insights/blog/microservices/modular-monolith-better-way-build-software
- Team Topologies – Key concepts: https://teamtopologies.com/key-concepts
发表回复