SpringBoot项目如何分包-按业务域组织代码

📅 2026/8/25 13:53:29
SpringBoot项目如何分包-按业务域组织代码
Spring Boot 项目如何分包按业务域组织代码摘要传统按技术层分包会把同一业务的入口、服务、DAO 和对象拆散到项目各处。本文结合 MetaLite 的module1与sys包结构分析为什么先按业务域聚合、再在域内分层以及单服务单 Maven 工程与框架组件独立拆分为什么并不冲突。很多 Spring Boot 项目从下面的目录开始controller/ service/ dao/ entity/ dto/项目较小时这种结构非常直观。所有 Controller 在一个目录所有 Service 在另一个目录。随着业务增长问题逐渐出现一次“修改用户权限”的需求需要分别进入 controller、service、dao、dto、entity 和 converter在几个包含几十甚至上百个类的目录之间来回跳转。技术层次很清楚业务边界却被拆散了。MetaLite 的后端服务采用另一种顺序先按业务域分包再在域内划分入口、Service、DAO 和 Domain。一、按技术层分包解决了什么按层分包的优点是真实存在的新人容易理解 MVC 分层同类技术代码集中小项目目录简单制定统一规范比较直接。例如controller/UserController.java service/UserService.java dao/UserDao.java entity/UserEntity.java当系统只有几个业务对象时文件位置几乎不需要思考。问题出现在业务域数量和团队规模增长以后。顶级目录表达的是“这是什么技术类”却没有表达“它属于哪块业务”。二、一次业务变更为什么会横跨整个项目假设系统同时包含用户、组织、订单、结算、营销和通知。按技术层分包后可能变成controller/ UserController OrgController OrderController SettlementController ... service/ UserService OrgService OrderService SettlementService ... dao/ UserDao OrgDao OrderDao SettlementDao ...一次订单需求会纵向穿过每个顶级目录。排查问题时开发者不断在技术层之间切换同时还要过滤大量无关业务类。更麻烦的是业务边界不清时Service 很容易跨域直接调用 DAO公共目录逐渐变成所有模块都能引用的“共享区”。三、先按业务域聚合再在域内分层MetaLite Demo 的当前结构是com.metalite.demo/ └── module1/ ├── entrance/ │ ├── api/ │ ├── consumer/ │ └── job/ ├── service/ ├── dao/ └── domain/ ├── param/ ├── dto/ └── entity/管理服务则以sys作为业务域com.metalite.admin/ └── sys/ ├── entrance/ ├── service/ ├── dao/ └── domain/这种方式通常被称为 package by feature 或按业务模块分包。它没有取消技术分层只是调整了优先级先回答“属于哪个业务域” 再回答“在该域中承担什么技术职责”四、为什么 API、MQ 和 Job 都放在 entrance一个业务能力可能通过多种方式触发HTTP APIMQ 消费定时任务。如果只把 Controller 当作入口Consumer 和 Job 往往会散落到独立顶级目录业务边界再次被拆开。MetaLite 使用entrance/api entrance/consumer entrance/job它表达的是这些代码都负责把外部触发转换为当前业务域的一次执行只是触发协议不同。需要如实说明当前 Demo 和 Admin 中有 Job 直接调用 DAO 的实现因此源码并没有严格执行“所有入口只能调用 Service”。文章把 entrance 描述为组织原则而不是当前已经由编译器强制的分层规则。五、单服务单 Maven 工程为什么更容易排查MetaLite 的backend-demo和backend-admin都是各自一个 Maven 工程和一个主要部署 Jar。服务内部不再机械拆成xxx-api xxx-service xxx-dao xxx-domain对于共同构建、共同发布、共同部署的一个微服务包结构通常已经能够表达内部职责。继续拆多个 Maven 模块会增加POM 依赖关系构建顺序跨模块重构循环依赖处理IDE 导航和调试上下文。这不是说 Maven 多模块没有价值。如果某个 SDK 需要独立发布某个插件拥有独立生命周期或者不同团队拥有清晰所有权它仍然适合独立模块。是否拆分应由发布和所有权边界决定而不是看到一个技术层就创建一个 Maven module。六、框架组件拆工程与业务服务单工程并不冲突MetaLite 自身的 BOM、Application、ORM、MQ、Gateway、Admin 和 Web Admin 分别承担不同技术能力具有跨服务复用或独立演进价值因此分开维护。单个业务服务则保持一个主要 Maven 工程。两者判断标准一致需要独立复用、独立发布和独立演进的能力才形成工程边界共同发布的业务代码优先使用包边界。所以“框架组件拆开”和“服务内部不机械拆模块”不是相反原则而是对不同层级应用同一个原则。七、跨服务共享对象怎样避免依赖完整实现服务间确实需要共享请求参数和返回对象。backend-admin与backend-demo在打包主 Jar 的同时额外通过 Maven classifier 生成clientJarclassifierclient/classifierincludesincludecom/metalite/admin/*/domain/**/include/includes网关可以依赖backend-admin:client获得 Domain 类型而不把 Service、DAO 和配置一起带入依赖图。当前 classifier 包含整个domain目录其中既有 Param、DTO也包含 Entity。因而准确说法是“隔离业务实现代码”不能宣传成“只暴露纯 DTO实体绝不外泄”。八、按业务域分包也有代价这种结构不是零成本方案。相同技术层代码分散在不同业务包横向修改所有 API 或所有 DAO 时需要跨业务域搜索业务域划分错误会导致模块之间频繁互相引用小项目只有一个简单业务时额外业务层级可能显得多余。因此关键不是把controller政名为entrance而是先形成稳定业务边界。如果一个类同时被多个业务域修改或者一个需求总要穿过多个域可能说明边界划分需要重新审视。九、排查问题时目录应该提供业务上下文当线上出现“组织权限修改后用户菜单没有刷新”时按业务域结构可以先进入admin/sys/入口、权限 Service、关联 DAO 和 Domain 都位于这个上下文中。开发者仍需沿调用链排查但不需要先在全局技术目录中过滤其他业务。项目结构无法自动解决代码质量问题却能降低每次定位时需要装入大脑的无关上下文。这也是我设计 MetaLite 工程结构时更看重的指标不是目录是否符合某张经典分层图而是一次业务开发和故障排查能否尽量停留在明确范围内。框架简介MetaLite 是面向企业生产环境的新一代 Java 微服务技术底座。系列文章重点分享代码背后的设计思路、技术取舍与工程实践。源码基线JDK 21、Spring Boot 3.2.9、Spring Cloud 2023.0.1、Spring Cloud Alibaba 2023.0.1.3具体组件版本以项目backend-bom为准。作者简介15 年 Spring 体系企业级开发经验专注于 Java 微服务架构、工程治理与生产实践。持续更新MetaLite 系列内容将持续更新围绕核心设计、源码链路、技术取舍与生产实践展开。欢迎关注作者及时获取后续内容。在线演示演示地址: https://admin.metalite.top/演示账号: guess演示密码: admin2026