跳到主要内容
约 4 分钟2 张卡

模块化单体

模块化单体在一个部署单元内划分业务模块。本文讲解模块接口、依赖约束和事务内外的事件。

1. 面试核心​

先记住这几条,再读正文
  • 部署与模块边界。 模块化单体共享部署单元,各模块只公开业务接口。卡 · 综合题
  • 依赖与数据所有权。 单向依赖和唯一数据写入方构成模块边界。
  • 结构校验。 Spring Modulith 的结构验证可检查依赖环与内部包越界。
  • 事件可靠性。 普通内存事件会随进程退出丢失,持久事件登记支持恢复。卡 · 事务事件与持久登记

2. 业务模块​

按业务能力分组后,一个订单需求集中在订单模块。模块内部再安排应用、领域和基础设施代码。

com.example.shop
├── order
│ ├── OrderManagement.java
│ └── internal
├── inventory
│ ├── InventoryManagement.java
│ └── internal
└── payment
├── PaymentManagement.java
└── internal

模块分别拥有规则、接口和内部实现。它们共享进程与发布流水线。

3. 模块接口​

公开 API 表达业务能力,例如 reserveInventory。Repository 和持久化实体留在模块内部。公开类型越少,需要维护的外部契约越少。

Java 的 package-private 只约束同一个包。子包也有独立的访问边界。public 类型放在 internal 包中,仍需结构测试约束访问。

Spring Modulith 2.1 默认以根包的直接子包识别模块。模块根包中的公开类型作为 API。额外公开范围通过 named interface 声明。

4. 单向依赖​

A 调用 B,B 又引用 A,会形成依赖环。双方的测试、初始化和后续拆分随之耦合。

共同规则应归属实际负责的模块。稳定契约可单独抽出,后续动作可改用事件。共享包应保持小而稳定,避免吸收双方内部实现。

5. 同步调用与事件​

用例需要立即判断结果时,使用同步模块 API。只需通知后续工作时,可发布事件。事件处理时机决定事务关系。

Spring 默认同步分发应用事件。监听器异常可影响发布调用与原事务。提交后异步监听则有独立的失败窗口。

5.1 事务事件与持久登记​

事务提交后,进程可在监听器执行前退出。普通异步事件保存在内存,重启后缺少恢复依据。

Spring Modulith 2.1 提供 Event Publication Registry。配置持久化实现后,发布记录随业务事务写入。每个事务监听器对应一条处理记录。监听器成功后,登记项标记完成。

失败记录保留,可通过重发机制恢复。监听器仍需幂等,以处理执行成功后确认丢失。跨进程交付的机制见Transactional Outbox 与 CDC。

6. 数据所有权​

模块可以共用数据库实例。每张业务表仍由一个模块写入。其他模块通过 API 或只读投影获取数据。

schema、表名前缀和迁移脚本可以标记责任。跨模块事务可维护确需原子提交的规则。此类事务形成显式依赖,应随边界一起评审。

7. 自动化结构检查​

结构规则应进入 CI。以下测试检查模块依赖:

class ModuleStructureTests {
@Test
void verifiesModules() {
ApplicationModules.of(ShopApplication.class).verify();
}
}

Spring Modulith 2.1 检查依赖环与内部包越界。配置 allowedDependencies 后,还检查允许依赖列表。显式开放的模块允许内部类型访问。ArchUnit 可补充包依赖和自定义规则。

结构测试检查代码关系。跨模块 SQL 与运行时资源争用还需独立验证。

8. 模块的测试与观测​

模块测试覆盖公开 API、领域规则和依赖契约。Spring Modulith 可缩小集成测试的模块范围。

日志和指标加入模块维度。事件积压、调用耗时和事务失败应能定位到模块。所有模块共享进程,CPU 与堆内存仍会相互影响。

9. 适用场景​

边界仍在变化、团队规模有限时,模块化单体较易维护。它保留本地调用与共库事务的便利。接口、写入权和结构验证让边界可以持续检查。

某个模块有稳定的独立发布或隔离需求后,可评估拆分。目录拆开只是组织代码的一步。

10. 常见问题​

10.1 Maven 模块与业务模块​

Maven 模块提供构建边界。业务模块还需要明确契约、依赖和数据所有权。两者可以配合使用。

10.2 模块维护责任​

11 · 面试题卡片​

11.1 模块化单体与微服务怎样选择​

出现公司:字节跳动、美团

题型:综合题

考察重点

  • 逻辑模块与部署单元的区别。
  • 契约、依赖、数据所有权和结构检查。
  • 独立部署收益与分布式成本。

相关内容:业务模块至适用场景。

参考回答

模块化单体适合边界仍在变化且运维资源有限的系统。

模块共享进程,本地调用与事务较简单。微服务适合独立发布、扩容或隔离收益明确的能力。

两者都需要业务接口和数据所有权。单体可用单向依赖及 CI 结构测试维护边界。服务拆分则增加网络失败、契约兼容和运行成本。选择依据是可验证的收益与维护能力。

11.2 模块事件在事务提交后处理,进程崩溃时如何恢复?​

出现公司:暂无已核验的公开来源(本站自测)

考察重点:监听时机、持久登记与重复处理。

相关内容:事务事件与持久登记。

参考回答

持久事件登记为重启后的恢复提供依据。

业务变化与发布记录在同一事务中提交。监听器完成后标记结果,未完成记录用于重发。普通内存事件缺少这份记录。监听器仍需幂等,以覆盖成功后确认丢失。

12. 参考资料​