单体项目越写越乱?Spring Modulith 用一条测试守住模块边界
一个 Spring Boot 项目未必需要立刻拆成微服务,但“所有包都能调用所有包”的单体也会越来越难维护。Spring Modulith 的切入点是把应用中的业务模块识别出来,并在测试中检查模块依赖。项目由 Spring 团队维护,仓库注明采用 Apache 2.0 许可证;截至本稿核验,官方参考文档显示的稳定版为 2.1.1。这不是“自动把代码变成微服务”的工具,而是帮助团队把模块边界变成可检查的约束。
最小起点:按业务包组织代码
默认策略以应用主类所在包的直接子包识别模块。例如 com.example.shop.order 与 com.example.shop.inventory 可以是两个模块,模块根包中的公开类型构成对外接口,internal 等子包中的实现通常不应被其他模块直接依赖。实际识别规则与可定制方式见官方基础概念。
在已有 Spring Boot 测试项目中,可先引入 spring-modulith-core,并按当前项目的依赖管理配置 2.1.1 版本或相应 BOM。下面只展示验证方法,未在本仓库实际编译运行:
package com.example.shop;
import org.junit.jupiter.api.Test;
import org.springframework.modulith.core.ApplicationModules;
class ModuleBoundaryTests {
@Test
void modulesRespectBoundaries() {
ApplicationModules.of(ShopApplication.class).verify();
}
}把测试放在能访问 ShopApplication 的包下,并确认 spring-modulith-core 在测试类路径里。verify() 会检查模块之间是否存在循环、其他模块是否越过公开接口访问内部类型;若通过 @ApplicationModule(allowedDependencies = …) 声明了允许依赖,还会检查这些约束。发现违规时,它会让测试失败。规则来自官方验证文档。
适用场景与边界
如果团队已有清晰的业务包结构,先加一条边界测试的投入较小,适合在持续集成中防止新代码重新耦合。对于历史项目,第一次运行可能暴露大量存量跨包引用;应先确定哪些类型是真正的模块 API,再逐步整理。官方也提供“开放模块”等过渡能力,但开放访问会减弱内部类型的隔离,不能长期把它当作跳过架构整理的捷径。
这项验证关注代码结构,不会证明业务事务、消息交付或服务之间的运行时行为正确。它也不会自动判断一个模块划分是否符合团队的业务边界;这仍需要开发者命名和审查。引入前最好在一条真实业务链上试验:选两三个子包,运行验证,记录当前违规项,决定哪些是设计问题、哪些需要通过显式接口开放。