Layered、Package by Feature 与领域边界

按 Controller、Service、Repository 分包容易上手,但项目变大后,同一个业务可能分散得很远。下面比较按功能组织代码的方式,并说明模块内部仍然可以分层。重点是让一次业务改动有清楚的范围。

版本与资料依据

  • last_verified: 2026-08-24
  • version_scope: stable architecture concepts
  • source_type: architecture literature + engineering synthesis
  • stability: stable-concept

controller/service/repository 全局分层容易让一个业务变更跨越全仓库。Package by feature 以 orders, inventory, billing 聚合代码,再在模块内部使用 adapter/application/domain 分层。

Text
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 查阅。下面的链接支持本节的机制说明;练习场景和选型建议是本教程的教学设计。