模块化单体是什么
模块化单体在单个部署单元内按业务子域划分模块,模块之间只能通过显式接口通信,禁止跨模块访问内部实现。它保留了单体在部署、调试、事务上的简单性,同时用编译期或构建期约束把边界固化下来,是走向服务化之前更稳妥的中间形态。
为什么值得先做
直接在单体上拆服务,往往会发现代码里根本没有清晰边界,只能按技术分层切开,结果得到一堆必须同步发布的分布式组件。先在单体内把模块边界与数据所有权理顺,拆分时才有真实存在的接缝可以下刀,回滚成本也低得多。
基本信息
| 边界手段 | 模块接口、包结构约束、构建期依赖检查 |
|---|---|
| 数据 | 单库,但按模块划分表的所有权 |
| 调用规则 | 跨模块只能调用公开接口,内部实现不可见 |
| 演进价值 | 边界稳定后可平滑演进为服务化 |
实践要点
- 按子域划分模块,以业务能力命名,不按技术分层
- 显式接口:模块只暴露接口包,内部实现包禁止外部引用
- 构建期约束:接入依赖检查工具,违规引用直接让构建失败
- 数据所有权:表归属单一模块,跨模块取数必须走接口
- 接口兼容:模块接口变更走扩展-收缩,避免同步改多处
- 演进路径:边界稳定后优先拆分读多写少、团队独立的模块
依赖检查工具
| 技术栈 | 工具 |
|---|---|
| Java | ArchUnit |
| Python | import-linter |
| C# 与 .NET | NetArchTest |
| JS 与 TS | dependency-cruiser |
与普通单体的差别
普通单体往往按技术分层组织代码,业务规则散落在控制器、服务与数据访问层里,改一个业务需求要动所有层。模块化单体按业务子域组织代码,一个子域的领域逻辑、接口与数据归属集中在一起,交付新需求通常只落在单个模块内。这个差别决定了它是通往服务化的跳板,还是又一个需要重构的遗留系统。
模块化单体的验收标准是:能否在不改动其他模块的前提下替换某个模块的实现。做不到这一点,说明边界还停留在目录命名上。