Modular Monolith 模块化单体架构学习与实践指南(awesome-software-architecture 精选资源深度解读)
【免费下载链接】awesome-software-architecture📚 A curated list of awesome articles, videos, and other resources to learn and practice software architecture, patterns, and principles.项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-software-architecture
导读
模块化单体(Modular Monolith)是一种试图同时吸收单体与微服务两种架构优点的折中方案:它以单一代码库、单一部署单元与单一数据库交付系统,却在内部以清晰的模块边界、明确的职责与接口进行组织,从而保留团队独立开发、独立测试的能力,并保留后续向微服务演进的路径。本篇指南以 awesome-software-architecture 仓库中 docs/modular-monolith.md 的精选资源为骨架,结合仓库内 DDD、事件驱动、Outbox、反模式等配套文档,系统讲解模块化单体的定义、架构驱动因素、模块边界划分、模块间通信、架构强制手段以及向微服务演进的路线,并整理出一份可直接对照学习的资源导航与示例项目清单。读完本文,你将能够判断自己的项目是否适合采用模块化单体,并掌握落地它所需的核心概念与学习路径。
一、什么是模块化单体:定义与核心特征
awesome-software-architecture 仓库在 README.md 中对本主题给出了精确定义:
Modular Monolith is an architectural approach that combines the advantages of monolithic and microservices architectures. It aims to build a monolithic application with a modular design that allows it to be divided into smaller, more manageable parts, each with its own clear responsibilities and interfaces. This approach allows teams to develop and deploy features independently, while still maintaining a single codebase and database. The modular design also facilitates the testing and maintenance of the application, as well as the scaling of individual modules.
即:模块化单体是单体形态 + 微服务思维的结合体。它具备以下核心特征:
- 单一代码库与单一部署单元:整个系统仍然作为一个进程、一个应用部署,运维模型与传统单体一致;
- 模块化设计:应用内部被划分为若干职责清晰、接口明确的模块(Module),每个模块都可以被独立理解、测试与维护;
- 团队协作友好:不同团队可以在同一代码库中围绕各自模块独立开发,减少相互阻塞;
- 数据库统一但逻辑隔离:共享一个数据库,但数据访问与领域逻辑按模块隔离;
- 可演进性:模块边界一旦清晰,未来可以按模块逐个拆分出去演化为微服务。
它和"传统单体"(即 Big Ball of Mud 大泥球反模式)的本质区别在于:有没有显式的模块边界与依赖规则。代码 opinion 社区的经典论断"Long live the Monolith! Monolithic Architecture != Big Ball of Mud"正是强调——单体架构本身不是问题,失去边界、纠缠不清的单体才是问题。
三种架构形态的直观对比
| 维度 | 传统单体(大泥球) | 模块化单体 | 微服务 |
|---|---|---|---|
| 部署单元 | 单个 | 单个 | 多个独立部署 |
| 代码组织 | 无边界、相互纠缠 | 显式模块、清晰接口 | 独立服务、独立仓库 |
| 团队自治 | 低 | 中(模块级自治) | 高(服务级自治) |
| 运维复杂度 | 低 | 低 | 高 |
| 独立扩展 | 不支持 | 有限(可水平复制整体) | 支持按服务扩展 |
| 演进成本 | 拆分困难 | 可按模块渐进拆分 | 已拆分 |
二、为什么选择模块化单体:架构驱动因素
Kamil Grzybek 的《Modular Monolith: Architectural Drivers》(该主题的经典文章,已收录于本仓库精选清单)系统梳理了选择模块化单体的架构驱动因素。综合本仓库 微服务文档 与 事件驱动架构文档 的论述,常见驱动因素包括:
1. 团队规模与组织形态不匹配微服务
微服务的主要收益之一是"让团队独立交付",但其代价是分布式系统固有的复杂度:网络故障、分布式事务、服务发现、可观测性等。当团队规模较小(如一到两个团队)时,直接上微服务往往得不偿失——这正是 Martin Fowler《MonolithFirst》的核心观点:除非有明确需要,否则先从单体开始,而"带好边界的单体"(即模块化单体)是最佳的中间形态。
2. 需要保留单一事务边界
模块化单体共享同一个数据库与同一个事务管理器,跨模块的业务操作可以保持 ACID 事务。而微服务一旦拆分数据库,就必须面对分布式事务与最终一致性的复杂问题。若业务强一致性需求较多,模块化单体是更务实的选择。
3. 开发体验与启动成本
相比微服务,模块化单体只需启动一个进程,本地调试、端到端测试、重构工具链都简单得多。仓库清单中 [Easy Modular Monolith 系列文章](MVP、Outbox、日志、全局异常处理、JWT 认证等六篇)正是围绕"如何低成本地把一个模块化单体搭建起来"展开的实战教程。
4. 保留向微服务演进的选项
模块化单体最重要的价值之一是"演化优先":当某个模块的负载、团队或发布频率确实需要独立扩展时,可以只拆这一个模块,而不是像大泥球单体那样推倒重来。仓库清单中 Chris Richardson 的《Decompose Your Monolith》系列与 Milan Jovanović 的《Breaking It Down: How to Migrate Your Modular Monolith to Microservices》都演示了这种渐进式拆分路径。
何时应该直接选择微服务
结合仓库 微服务文档 的论述,当出现以下信号时,才应考虑真正拆分微服务:
- 存在多个需要独立扩展、独立发布的高竞争模块;
- 团队规模足够大,每个服务都能由独立团队长期维护;
- 已经具备分布式运维能力(容器编排、服务网格、可观测性等);
- 业务允许跨服务采用最终一致性模型。
三、模块化单体的核心设计原则
3.1 模块边界:用有界上下文划分模块
模块划分是模块化单体的第一要务。DDD(领域驱动设计)中的有界上下文(Bounded Context)是划分模块边界的黄金标准:每个模块对应一个独立的有界上下文,拥有自己的通用语言(Ubiquitous Language)与领域模型。仓库中的 有界上下文文档 与 战略设计模式文档 给出了判断边界的系统方法:通过事件风暴(Event Storming)等建模手段识别业务能力,进而确定每个上下文的范围与彼此之间的关系(共享内核、防腐层、开放主机服务等)。
Kamil Grzybek 的参考实现 modular-monolith-with-ddd(本仓库清单首推的完整示例)正是"按有界上下文划分模块"的教科书级示范:每个模块独立包含自己的领域层、应用层与基础设施层,模块之间禁止跨边界直接引用领域对象。
3.2 模块内高内聚、模块间低耦合
- 内聚:一个模块内的代码应围绕同一业务能力组织,即所谓"按功能/切片组织而非按技术分层组织"。这与仓库中 垂直切片架构文档 的思想一致——一个模块内部可以由若干垂直切片组成,每个切片覆盖从 API 到持久化的完整请求路径;
- 耦合:模块间只能通过公开接口(模块对外暴露的 API 或消息契约)交互,禁止模块 A 直接读取模块 B 的数据库表,也禁止 A 直接 new 出 B 的内部类。仓库清单中 Lukasz Reszke 的《SHARING DATA BETWEEN MODULES IN MODULAR MONOLITH》专门讨论了模块间数据共享的边界问题(共享什么、不共享什么、如何共享)。
3.3 模块间通信:同步调用与异步消息
模块间通信是模块化单体中最容易被做坏的部分,主要有两种模式:
同步通信:模块 A 直接调用模块 B 提供的公开服务接口(如方法调用或内部 HTTP/契约调用)。优点是简单直观、结果立等可取;缺点是形成运行时耦合,且调用链变长后难以独立扩展。仓库清单中 [Easy Modular Monolith 系列] 的 Part 6 专门讲解模块间同步通信的实现方式。
异步通信(事件驱动):模块通过发布集成事件(Integration Events)解耦。仓库中的 集成事件文档 与 事件驱动架构文档 详细介绍了这一模式:事件发布方只负责发布"发生了什么事",订阅方自行决定如何响应,双方互不知晓对方存在。同时,领域事件文档 区分了领域事件(进程内、领域层内部)与集成事件(跨模块/跨服务边界)——在模块化单体内,模块间通信应使用集成事件,而领域事件主要用于模块内部的领域逻辑编排。
可靠消息投递:Outbox 模式。事件发布与业务数据提交必须保持原子性,否则会出现"数据已保存但事件没发"或反之的一致性漏洞。仓库中的 Outbox 模式文档(又称事务性发件箱/Transactional Outbox)给出了标准解法:在同一数据库事务中写入业务数据与"待发送事件"到 outbox 表,再由后台进程(或 CDC 变更数据捕获)将事件投递到消息中间件,实现至少一次投递(at-least-once)。[Easy Modular Monolith 系列] 的 Part 2 正是以 Outbox 模式为主题。
3.4 架构强制(Architecture Enforcement)
"模块边界"若只停留在约定层面,最终必然腐化。Kamil Grzybek 的《Modular Monolith: Architecture Enforcement》一文专门讨论如何用工具与测试强制架构规则,常见手段包括:
- 依赖方向控制:通过架构测试(如 .NET 的 NetArchTest、Java 的 ArchUnit)断言模块依赖图,禁止反向依赖与跨模块引用;
- 模块可见性限制:利用编译层面的访问修饰符(如 C# 的
internal与InternalsVisibleTo)将模块内部类型对外不可见,只暴露明确的公开门面(Facade); - 独立解决方案/独立程序集:Jon P Smith 的《Evolving modular monoliths: 2. Breaking up your app into multiple solutions》展示了把每个模块拆成独立程序集甚至独立解决方案的做法,从物理结构上保证模块边界不可绕过;
- CI 校验:在持续集成流水线中加入架构规则检查,一旦违反即构建失败。
3.5 与 DDD、CQRS、Clean Architecture 的结合
模块化单体与多种架构风格天然互补,仓库清单与配套文档均可佐证:
- DDD:用有界上下文划模块,用聚合、值对象、领域事件等战术模式充实模块内部。参考 DDD 文档、战术设计模式文档 以及示例 AwesomeBank(.NET 5.0 + DDD + CQRS + 模块化单体);
- CQRS:模块内部可分别优化读写模型,参考 CQRS 文档;
- Clean Architecture:Jon P Smith 的《My experience of using the Clean Code architecture with a Modular Monolith》与 Clean Architecture 文档 探讨了在模块内部应用依赖倒置与分层的问题;
- 垂直切片:模块内部按业务功能垂直组织,参考 垂直切片架构文档 及示例 DDD-VShop(每个模块都是带自定义架构的独立垂直切片)。
四、好的单体与坏的单体:警惕 Big Ball of Mud
模块化单体最大的风险是退化为大泥球。仓库 Big Ball of Mud 文档 指出,大泥球是"没有任何可识别的架构、边界模糊、代码相互纠缠"的反模式。判断一个单体是"好单体"还是"坏单体",可以看:
| 好单体(可称为模块化单体) | 坏单体(大泥球) |
|---|---|
| 有显式模块边界与公开接口 | 无边界,任意类可互相引用 |
| 依赖方向受控、单向 | 依赖图混乱、循环依赖 |
| 数据访问按模块隔离 | 各层代码直连共享数据库表 |
| 有架构测试强制规则 | 依赖代码评审口头约定 |
| 可按模块独立测试 | 牵一发而动全身 |
Tomas Tulka 的《Good and Bad Monolith》与 CodeOpinion 的《Long live the Monolith! Monolithic Architecture != Big Ball of Mud》均以"好/坏单体"二分法为主题,是理解这一区别的最佳入门材料。仓库中 内聚与耦合文档、耦合文档 也可作为模块设计的理论依据。
五、演进路线:从单体到模块化单体再到微服务
5.1 MonolithFirst:先单体,后拆分
Martin Fowler 的《MonolithFirst》主张:在缺乏明确拆分理由时,应先构建单体;即使未来需要微服务,从一个模块化良好的单体出发也比从零设计分布式系统容易得多。仓库清单中《Build the modular monolith first》进一步强化了这一观点:把"模块化单体"作为默认起点。
5.2 渐进式分解的十条原则
Chris Richardson 在《Decompose your monolith: Ten principles for refactoring a monolith to microservices》中给出了从单体分解为微服务的原则框架,与《Decompose Your Monolith: Strategies for Migrating to Microservices》互为补充。核心要点包括:围绕业务能力与有界上下文切分、先以模块/代码边界开始、用 Strangler Fig(绞杀者模式)渐进替换、优先处理数据所有权等。仓库中的 Strangler Fig 模式文档 与 模块间通信/服务边界 可作为配套参考。
5.3 从模块化单体到微服务的具体步骤
Milan Jovanović 的《Breaking It Down: How to Migrate Your Modular Monolith to Microservices》给出了从模块化单体拆分的实战路径,概括为:
- 模块边界先行:确保每个待拆模块已经拥有完整、独立的数据与逻辑边界(这正是一开始就采用模块化单体的意义所在);
- 通信契约化:将模块间同步调用逐步替换为事件/消息契约,使拆分后仍能通信;
- 数据拆分:把共享数据库按模块拆分为独立库,通过事件溯源、CDC等手段迁移数据;
- 独立部署:逐个将模块抽取为独立服务,利用 API 网关、服务发现等基础设施补齐分布式能力;
- 基础设施补齐:分布式事务采用 Saga,参考分布式事务文档与最终一致性文档。
需要强调的是,拆分应是按需、增量的:只有当某个模块确实需要独立扩展/独立发布时才拆,切忌为了"微服务而微服务"。
六、仓库精选资源导航
以下分类导航覆盖 docs/modular-monolith.md 中的全部精选资源(文章、视频与示例项目)。该文档是持续维护的完整清单,建议对照原文档获取最新链接与标注。
6.1 基础资源与入门必读
- Simon Brown《Modular Monoliths》:GOTO 大会演讲幻灯片,模块化单体概念的奠基性材料,另配有 2016/2018 等多个演讲视频;
- Kamil Grzybek《Modular Monolith: A Primer》:模块化单体入门经典,系统介绍定义、动机与结构;
- Kamil Grzybek《Modular Monolith: Architectural Drivers》:深入分析选择该架构的驱动因素与权衡;
- Kamil Grzybek《Modular Monolith: Architecture Enforcement》:讲解如何用工具与测试强制架构规则;
- Martin Fowler《MonolithFirst》:先构建单体的方法论论据;
- 《Modular Monolith - A Gentle Introduction》与《Implementation Deep Dive》(DanDoesCode):入门到实现的配套阅读;
- 《Making Modular Monoliths Work》(Sookocheff):讨论让模块化单体真正生效的实践条件;
- 《A Practical Guide to Modular Monoliths with .NET》(chrlschn.dev)⭐:仓库清单标注的推荐实践指南;
- 《Modular programming: Beyond the spaghetti mess》(Tiny.cloud):从模块化编程原理出发理解模块化单体。
6.2 架构权衡与定位
- CodeOpinion 系列:《Loosely Coupled Monolith Overview》《Long live the Monolith! Monolithic Architecture != Big Ball of Mud》《Scaling a Monolith Horizontally》——分别讨论松耦合单体概览、单体≠大泥球、单体水平扩展;
- 《Why using Microservices or Monolith can be just a detail?》(ThreeDotsLabs):指出单体与微服务的选择本质上应服从于业务与团队约束;
- 《Thoughts on "Modular Monoliths"》《Actually Talking about Modular Monoliths》《Modular Monoliths and the "Critter Stack"》(Jeremy D. Miller):对模块化单体的批判性思考与工具栈讨论;
- 《Majestic Modular Monoliths》(Lukas Hajdu):探讨"雄伟"的模块化单体形态,另有 Axel Fontaine 的同名演讲视频;
- 《Improving Monolith's Availability》《Scaling a Monolith Horizontally》:讨论单体的可用性与扩展性提升手段;
- 《Good and Bad Monolith》(Tomas Tulka):好单体与坏单体的判别;
- 《How to quickly scale a legacy monolith?》(event-driven.io):以事件驱动方式快速扩展遗留单体。
6.3 模块设计、通信与数据共享
- 《SHARING DATA BETWEEN MODULES IN MODULAR MONOLITH》(Lukasz Reszke):模块间数据共享的边界问题;
- 《Event Modeling & Modular Monolith | From colored cards to code through TDD》:用事件建模 + TDD 从卡片到代码落地模块化单体;
- 《My experience of using modular monolith and DDD architectures》与《My experience of using the Clean Code architecture with a Modular Monolith》(Jon P Smith):DDD 与 Clean Architecture 在模块化单体中的真实使用经验;
- 《Evolving modular monoliths: 1. An architecture for .NET》与《Evolving modular monoliths: 2. Breaking up your app into multiple solutions》(Jon P Smith):.NET 下模块化单体的架构演进与多解决方案拆分。
6.4 端到端实战系列(Easy Modular Monolith)
- Part 1 — MVP:从最小可行产品起步搭建模块化单体;
- Part 2 — OutBox Pattern:用事务性发件箱保证事件可靠投递;
- Part 3 — Logging(Serilog 和 Seq):日志与可观测性接入;
- Part 4 — Global Exception Handling:全局异常处理;
- Part 5 — JWT Authentication/Authorization:认证与授权;
- Part 6 — Synchronous communication between modules:模块间同步通信。
该系列完整覆盖了一个模块化单体从零到可用的全部关键环节,是最适合动手跟练的实战教程。
6.5 演进与迁移
- InfoQ《Migrating Monoliths to Microservices with Decomposition and Incremental Changes》:分解与增量变更的迁移方法论;
- Chris Richardson《Decompose your monolith: Ten principles…》与《Decompose Your Monolith: Strategies…》:单体分解的十条原则与迁移策略;
- Milan Jovanović《Breaking It Down: How to Migrate Your Modular Monolith to Microservices》:从模块化单体到微服务的迁移步骤;
- 《Monolithic to Microservices Architecture with Patterns & Best Practices》:模式与最佳实践视角的迁移综述;
- 《Build the modular monolith first》:把模块化单体作为默认起点的论证。
6.6 视频资源(精选)
- Simon Brown 系列:GOTO 2018《Modular Monoliths》、2016 版及专题演讲——概念奠基;
- Sam Newman《GOTO 2019 Monolith Decomposition Patterns》:单体分解模式;
- 《GOTO 2016 From Monolith to Microservices at Zalando》:大型电商平台真实迁移案例;
- CodeOpinion 系列:《Creating a Loosely Coupled Monolith》《Solution & Project Structure of a Loosely Coupled Monolith》《Asynchronous Messaging in a Loosely Coupled Monolith》《Avoiding a Big Ball of Mud! Coupling in a Monolith》——松耦合单体的创建、结构、异步通信与防腐化;
- 《START with a Monolith, NOT Microservices》:为什么从单体开始;
- 《Message Driven Architecture to DECOUPLE a Monolith》:用消息驱动架构解耦单体;
- 《Deconstructing the Monolith》(Shopify Unite 2019):大型单体拆解的工程实践;
- 《Building that glorious monolith. And carving it too.》(NDC Oslo 2022)⭐与《A Practical Guide to Modular Monoliths with .NET - Build Web Scale Monolithic Architectures》⭐:仓库清单重点标注的进阶演讲;
- 《How to design and code a scaleable solution (from monolith to microservices)》:从单体到微服务的可扩展设计编码;
- 《Scaling Monolithic Applications》《Splitting up a Monolith to (micro)Services》:扩展与拆分专题。
6.7 示例项目(按技术栈分类)
DDD 驱动的 .NET 模块化单体(学习首选)
- kgrzybek/modular-monolith-with-ddd:完整的 DDD 模块化单体参考实现,模块划分与架构强制的最佳范本,配套 React 前端仓库 modular-monolith-with-ddd-fe-react;
- marcinstelmach/AwesomeBank:.NET 5.0 + DDD + CQRS 的银行系统;
- DijanaPenic/DDD-VShop:.NET 6 模块化单体,每个模块是独立的垂直切片;
- kamilbaczek/Estimation-Tool 与 kamilbaczek/Modular-monolith-by-example ⭐:.NET 模块化单体示例(IT 估算工具业务域);
- evolutionary-architecture/evolutionary-architecture-by-example ⭐:以故事化方式讲解模块化单体与微服务、DDD 的交互;
- dcomartin/LooselyCoupledMonolith:松耦合单体实践;
- Nairda015/IGroceryStore:松耦合单体应用;
- DarekModzelewski/Contoso-University-DDD:经典 Contoso 大学示例的 DDD 化改造;
- chrisklug/asp-net-modular-monolith:ASP.NET 模块化单体示例;
- Ridikk12/ModularMonolith:模块化单体通用示例;
- PeterKneale/modular_monolith_saas:SaaS 场景的模块化单体。
.NET 8 现代模板与脚手架
- CharlieDigital/dn8-modular-monolith ⭐:.NET 8 模块化单体(momo)实战示例;
- baranacikgoz/modular-monolith-ddd-vsa-webapi:.NET 8 模板,整合模块化单体 + DDD + 垂直切片 + Clean Architecture;
- youssefbennour/AspNetCore.Starter:受 evolutionary-architecture 启发的模块化单体 Starter;
- phongnguyend/Practical.CleanArchitecture:同一业务在微服务、模块化单体、单体三种形态下的 Clean Architecture 对照实现。
电商/业务系统(真实规模参考)
- simplcommerce/SimplCommerce:.NET Core 模块化电商系统;
- nopSolutions/nopCommerce:基于 ASP.NET Core 的开源电商;
- grandnode/grandnode 与 grandnode/grandnode2:ASP.NET Core + MongoDB 的电商方案(含 headless、多租户版本);
- smartstore/SmartStoreNET:ASP.NET MVC 企业级电商;
- trueai-org/module-shop:模块化商城;
- thangchung/coolstore-moduliths 与 thangchung/coffeeshop-modular:coolstore/coffeeshop 业务的模块化实现;
- thangchung/blog-core:Blazor + 领域驱动模式的模块化博客;
- VirtoCommerce/vc-storefront:VirtoCommerce Storefront;
- ddd-by-examples/library:问题空间战略分析与战术模式的综合 DDD 示例。
其他语言生态
- anton-liauchuk/educational-platform:Java 模块化单体 + DDD;
- ttulka/ddd-example-ecommerce:Java + Spring 的 DDD 电商;
- ttulka/ddd-example-ecommerce-kotlin:Kotlin + Spring 版本;
- mgce/modular-monolith-nodejs:Node.js 模块化单体实现;
- stemmlerjs/ddd-forum:TypeScript 的 DDD 论坛应用;
- ThreeDotsLabs/monolith-microservice-shop:配合《Microservices or Monolith is just a detail?》文章的配套源码;
- drminnaar/chinook:架构、设计、.NET Core/TypeScript/React/Docker 的综合试验场。
七、学习路径建议
结合上述资源,推荐按以下顺序建立模块化单体的完整知识体系:
- 概念入门:先读 Simon Brown 的演讲材料与 Kamil Grzybek 的《A Primer》,建立"单体形态 + 模块思维"的整体认知;
- 边界设计:通过 有界上下文文档 与 战略设计模式文档 掌握模块划分方法,对照 modular-monolith-with-ddd 查看真实模块结构;
- 通信机制:阅读 集成事件文档、Outbox 模式文档 与 事件驱动架构文档,理解同步/异步通信与可靠投递;
- 动手实践:按 [Easy Modular Monolith 系列] 六篇文章从零搭建一个带认证、日志、异常处理与 Outbox 的完整模块化单体;
- 防腐化与演进:学习架构强制手段(Architecture Enforcement),再通过 Chris Richardson 与 Milan Jovanović 的迁移文章掌握"按需拆分为微服务"的增量路径,配套参考 Strangler Fig 模式文档 与 分布式事务文档;
- 持续跟进:回到仓库清单 docs/modular-monolith.md 获取持续更新的文章、视频与示例项目,并结合仓库内 微服务文档、DDD 文档、垂直切片文档 等主题深化理解,形成"单体起步、模块组织、按需拆分"的完整架构决策链。
结语
模块化单体并非"逃避微服务"的妥协方案,而是一种尊重业务边界、控制分布式复杂度、保留演进自由的架构选择。它用工程纪律(模块边界、依赖规则、架构强制)弥补了单体在组织层面的天然短板,又用单一部署与单一事务换来了微服务所不具备的简单性。对于大多数团队而言,"先构建一个模块化良好的单体,再按需演进"比一步到位地引入微服务更务实、更可持续。本仓库的精选资源与示例项目为这条路径提供了从理论到实践的全套弹药,值得系统研读。
【免费下载链接】awesome-software-architecture📚 A curated list of awesome articles, videos, and other resources to learn and practice software architecture, patterns, and principles.项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-software-architecture
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考