模块化单体用进程内边界保留部署简单性

说明模块化单体怎样通过依赖方向、数据所有权和公开接口形成边界,并识别何时才需要独立服务。

模块化单体用进程内边界保留部署简单性

模块化单体把系统作为一个主要部署单元运行,同时在内部建立明确模块。它保留本地调用、统一事务和较简单运维,又试图避免代码和数据任意耦合。关键不在“单体”名称,而在边界是否能够约束变化。

模块围绕一致责任形成

模块应对应稳定业务能力或技术责任,拥有自己的模型、规则和公开接口。按控制器、服务、仓库分成全局技术层,往往让一次业务变化跨越所有目录;按能力组织更容易把相关变化放在一起。

模块名称和职责要能被团队共同理解。过大的“公共”模块会吸收所有复用,最终变成隐性中心;过小模块则增加接口和导航成本。边界应通过变化原因、术语和数据所有权检验。

依赖规则必须可执行

调用方只能依赖目标模块公开接口,不能直接访问内部类或表。语言可见性、构建模块、静态分析和架构测试可检查依赖方向。仅靠目录命名无法阻止绕过。

循环依赖说明职责或协作方式有问题。可以提取明确协议、反转依赖或重划边界,而不是添加全局服务定位器。跨模块 DTO 应表达契约,不应直接暴露内部持久化实体。

共享基础库只容纳真正稳定、无业务所有权的能力。把每个重复函数立刻上移,会让模块通过共享类型重新耦合。

数据所有权比表的位置更重要

每张业务表应有主要写入模块,其他模块通过接口、只读视图或事件获得信息。所有代码都可直接连接同一数据库,并不意味着都应任意更新所有表。

单体可在一个数据库事务中协调多个模块,这对强一致流程很有价值,但频繁跨模块事务也暴露边界不足。事务编排应位于明确应用层,并避免一个模块依赖另一个模块的内部表结构。

模式可按 schema、命名和权限进一步隔离。即使暂不实施数据库权限,也应通过代码审查和测试保持所有权,为未来拆分保留路径。

进程内协作仍需要契约

同步函数调用简单、类型安全且易调试,适合需要立即结果的协作。领域事件可解耦一个动作后的多个响应,但进程内事件也要明确事务时机、顺序和失败处理。

异步并不是模块化的必要条件。为了模拟微服务而在单体内部引入消息基础设施,可能增加一致性与调试成本。选择应来自质量属性,而不是架构标签。

接口版本在同一部署单元内通常可原子升级,但测试仍要覆盖调用契约。模块替换或独立发布前,应先消除调用方对内部行为的隐式假设。

测试应证明边界和行为

模块单元测试验证内部规则,模块集成测试通过公开接口与真实数据库验证边界,少量端到端测试覆盖关键流程。测试若普遍直接构造内部对象,重构时会形成阻力。

架构测试可以禁止不允许的包依赖、反射访问和跨模块仓库调用。数据库迁移也应标记所属模块,并检查名称冲突与执行顺序。

启动时间和测试时间随单体增长可能上升,可通过模块级测试夹具和增量构建改善,而不是立即拆服务。

拆分服务需要具体驱动

独立扩缩容、不同可用性目标、监管隔离、发布节奏或团队自治可能推动拆分。单纯代码量大不是充分理由。拆分会引入网络故障、远程契约、数据复制和跨服务事务。

真正模块化的单体更容易拆分,因为接口和数据所有权已经存在。若内部边界混乱,直接分进程只会把隐式调用变成不可靠远程调用。

拆分前应测量瓶颈并形成质量属性场景。SEI 的架构分析方法强调从利益相关者关注和质量属性推导结构选择,而不是追随固定风格。

运维仍以一个发布单元为主

单一部署减少版本组合和网络跳数,但一次发布影响范围可能较大。需要模块级日志、指标和错误归属,使事件能够定位到内部责任区。

配置应按模块命名并验证,避免全局键冲突。资源使用也要可归因,否则某个模块的内存或线程泄漏会拖累整个进程。

模块化单体不是微服务前的临时状态。对于一致事务多、团队规模适中和部署简单性重要的系统,它可以是长期架构。成功标准是变化能否保持局部,而不是进程数量。

边界检查方法

模块边界可以通过静态依赖规则、公开接口测试和数据库访问审计持续检查。静态规则用于阻止跨层或跨模块直接引用,契约测试验证公开行为,运行时追踪则帮助发现绕过接口的隐式耦合。检查结果应落到具体依赖和所有者,不能只给出笼统的架构评分。

当一个变更频繁同时触及多个模块时,应先判断职责划分是否错误,还是业务流程本来就跨边界。前者需要调整模块归属,后者可由应用层协调。仅因跨模块调用存在就拆成远程服务,通常只会把进程内耦合转化为网络和数据一致性问题。

资料依据