高内聚业务模块应按业务域而非技术层划分,每个模块职责自足、边界清晰,通过 module-info.java 强制声明 exports/ requires,封装内部实现,仅暴露语义化 API,并用策略接口隔离变化点,就近管理数据与行为。

在 Java 模块化架构(JPMS)中设计高内聚的业务单元,核心不是把代码按目录拆开,而是让每个模块真正成为一个“职责自足、边界清晰、对外只说业务语言”的小系统。它得知道自己该做什么、不该碰什么,也不依赖别人替它做决定。
按业务域而非技术层切分模块
一个模块应围绕一个明确的业务能力组织,比如 com.example.order 负责订单创建、状态流转、取消与查询全生命周期;com.example.inventory 专注库存预留、扣减、回滚。不能出现 “order-service”、“order-dao”、“order-web” 这类按技术角色命名的模块——那是分层,不是划界。
模块内部可自由分层(Controller/Service/Repository),但对外只暴露语义清晰的 API 包,例如:
- exports com.example.order.api;(只导出 OrderService、OrderQuery 接口)
- 不 exports com.example.order.internal;(impl、config、aspect、dto 等一律不导出)
- 不提供通用工具模块(如 date-utils、json-helper),这些应由 JDK 或具体业务模块自行封装
用 module-info.java 强制声明契约与边界
模块的 module-info.java 是它的宪法,不是装饰。它必须显式回答三个问题:我能提供什么?我需要谁?我允许谁用我?
立即学习“Java免费学习笔记(深入)”;
- exports 只声明真正供外部协作的接口包,且接口本身需满足单一语义(如 PaymentProcessor 不掺杂日志或重试逻辑)
-
requires 列出直接依赖,仅限编译和运行必需;若本模块 API 的返回类型或参数类型来自被依赖模块(如返回
PaymentResult),则必须加 requires transitive - 禁止循环依赖:A requires B 且 B requires A 会被 JPMS 编译器直接拒绝——这是天然的边界守门员
把变化点隔离进策略接口,不在模块内硬编码分支
高内聚的本质是“稳住主干,放行变体”。当业务中存在多渠道、多规则、多实现时(如不同用户发不同券、多种运费计算方式),不要在模块内部写 if-else,而要抽象为策略接口,并由模块自身或外部模块提供实现。
- 定义 CouponIssuanceStrategy 接口,含
canApply(User)和issue(Order) - 各实现类(
NewUserCouponStrategy、VipCouponStrategy)放在各自职责明确的模块中 - 订单模块只持有 CouponIssuanceStrategy 接口,通过 ServiceLoader 或 DI 容器注入具体实现
- 新增一种策略,只需加实现类+注册,不改订单模块一行逻辑
模块即“小王国”:数据、行为、策略尽量就近
高内聚不是堆代码,而是让相关的东西物理上靠近、逻辑上绑定。一个业务单元应尽可能自己管理自己的状态与行为:
- 订单模块持有自己的 OrderEntity、OrderRepository 实现、校验规则、状态机定义
- 避免跨模块共享实体类(如把 Order 当 DTO 传给支付模块)——改用专用入参 DTO(
PayOrderRequest)和事件(OrderPaidEvent) - 领域事件(如
OrderCreatedEvent)用 record 或 final 字段定义,不可变、带版本号、只含关键事实字段 - 模块间通信走事件或接口调用,不直接访问对方数据库或内存结构


















