java应按业务域而非技术层划分包结构,如cn.edu.demo.order,并在域内分controller、service等子包;接口放api子包,实现放impl,禁止跨域直引实体,dto由提供方定义,公共内容仅限真正通用项。

Java 通过包结构划分清晰的业务模块,核心是让包名直接反映业务域,而不是技术分层。包即边界,一个包(或一组关联包)应完整承载某个可独立演进的业务能力。
按业务域建包,不是按技术层建包
避免把所有 Controller 放到 controller 包、所有 Service 放到 service 包。这种分法导致跨包调用泛滥、修改一处要动多个包,内聚低、耦合高。
- ✅ 推荐:每个业务域一个顶层包,如
cn.edu.demo.order、cn.edu.demo.payment、cn.edu.demo.user - ✅ 包内再分层:在
order包下设controller、service、dao、dto、domain等子包,只服务本域逻辑 - ❌ 避免:全局
com.xxx.controller下塞满所有控制器,导致 UserController 和 OrderController 虽然职责无关却强绑定在同一包中
接口与实现共置一域,显式表达依赖方向
接口不是“抽象出来放公共包就完事”,它的位置决定了谁可以依赖它。
- 对外暴露的能力,定义为接口,放在本域的
api子包(如cn.edu.demo.order.api.OrderService) - 实现类放在同域的
impl或默认包下(如cn.edu.demo.order.impl.DefaultOrderService) - 其他模块只能依赖
api包,禁止引入impl或dao;这样调用方不感知实现细节,也杜绝了循环依赖 - 若使用 Dubbo 或 Spring Cloud,
api包可单独打成 Jar 提供给消费者,物理隔离更彻底
模块间通信必须走契约,禁用跨域实体直引
两个业务模块交互,不能让 A 模块直接 new B 模块的 Entity 或调用其 Mapper。
- 跨模块调用统一通过接口 + 明确 DTO(如
OrderCreateCommand、PaymentResultDTO) - DTO 必须定义在提供方的
api包里,由提供方版本控制,消费方只引用该 DTO - 领域事件也是推荐方式:订单模块发
OrderPaidEvent,营销模块监听并处理,完全解耦 - 公共常量、基础异常等可抽到
common模块,但仅限真正通用、无业务语义的内容
包结构即架构图,目录层次要反映真实职责
打开项目源码,一眼就能看出系统有哪些核心业务能力,以及它们之间的关系。
- 顶层包名 = 业务能力名:
user(不是usercenter或usermanage),inventory,coupon - 包之间尽量平级,避免深度嵌套(如不用
cn.edu.demo.order.fulfillment.dispatch,而用cn.edu.demo.fulfillment单独成域) - 有明确上下文边界的,可用子域进一步区分:
user.auth(认证)、user.profile(资料),但前提是二者确实存在稳定、可分离的演进节奏 - IDE 中展开 src 目录,看到的是业务全景图,不是 MVC 技术流水线
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











