Layered、Package by Feature 与领域边界
按 Controller、Service、Repository 分包容易上手,但项目变大后,同一个业务可能分散得很远。下面比较按功能组织代码的方式,并说明模块内部仍然可以分层。重点是让一次业务改动有清楚的范围。
版本与资料依据
last_verified:2026-08-24version_scope:stable architecture conceptssource_type:architecture literature + engineering synthesisstability:stable-concept
controller/service/repository 全局分层容易让一个业务变更跨越全仓库。Package by feature 以 orders, inventory, billing 聚合代码,再在模块内部使用 adapter/application/domain 分层。
orders/
api/ # published API/events
internal/
web/
application/
domain/
persistence/
同一个词在不同业务里可能含义不同,Bounded Context 用来划定模型与业务语言适用的范围,不是要求每张表拆成一个服务。
应用服务组织一次用例,处理权限上下文与事务;领域服务承载不自然属于某一个实体的业务规则;仓库接口表达领域需要怎样保存和取得对象。先说明职责,再决定类与包的名字,会更容易避免形式上的分层。
验证依赖
- 模块外只能调用公开 API;
- domain 不依赖 Spring MVC/JPA annotations(是否完全纯净按成本决定);
- 禁止跨模块直接访问 internal repository/table;
- architecture test 或 Spring Modulith verification 检查 cycle 与暴露面。
“公开 API”不能只靠文件夹名字
把类放进 orders/api,并不意味着任何工具都自动把这个包当成公开接口。以 Spring Modulith 的默认模型为例,模块基础包与子包有不同含义;要额外开放命名接口,需要按其规则声明。先读工具的模块模型,再落到这里的示意目录。
做一个小验证:外部模块调用允许的入口应通过;直接引用内部实现应失败。文中的目录是组织思路,不是可以不加配置直接复制的 Spring Modulith 完整项目。
本节的核验依据
2026-10-25 查阅。下面的链接支持本节的机制说明;练习场景和选型建议是本教程的教学设计。