若依项目做到第三个月,admin 模块越来越臃肿------启动 40 秒、改一行代码全量编译、两个人改同一个文件天天冲突。这篇文章复盘我是怎么把若依从单模块拆成多模块的:哪个先拆、边界怎么画、依赖怎么管、趟过的坑一个不落。
一、单体项目的"三个月魔咒"
最开始搭若依的时候,目录干净得让人舒服。前两周没毛病,改什么都快。
问题从第一个月开始冒头:
| 症状 | 具体表现 | 谁难受 |
|---|---|---|
| 启动变慢 | 从 8 秒涨到 40 秒 | 每次调试都等得心烦 |
| 编译范围大 | 改一行 Service,全量 300+ 类重新编译 | 浪费时间 |
| 合并冲突 | 两个人都在 admin 里加东西,每天 merge 都有冲突 |
协作成本飙升 |
| 边界模糊 | CRM 的 Service 和 MES 的 Service 混在一起,越写越不敢删 | 谁敢重构谁死 |
到第二个月底,ruoyi-admin 已经塞了 CRM 模块和 MES 模块的全部代码------Controller 40 多个,Service 30 多个,Mapper 50 多个。
单模块不坏,但当你开始纠结"这个类该放哪个包"的时候,就是该拆的时候了。
二、若依框架的模块化设计全景
先看若依官方给的标准模块结构:
perl
ruoyi/
├── ruoyi-common/ # 公共工具类、异常、注解、枚举
├── ruoyi-framework/ # 框架核心配置(安全、数据源、Swagger)
├── ruoyi-system/ # 系统管理业务(用户、角色、菜单、部门)
├── ruoyi-generator/ # 代码生成器
├── ruoyi-quartz/ # 定时任务
└── ruoyi-admin/ # 启动模块 + Controller 汇总
依赖链路:只向下,不向上
perl
ruoyi-admin ──→ ruoyi-system ──→ ruoyi-framework ──→ ruoyi-common
(顶层) (业务层) (配置层) (基础层)
没有任何一个底层模块依赖上层模块。 这是 Maven 多模块拆分的第一铁律。
三、实战第一步:把业务模块从 admin 里拆出来
Step 1:创建新模块
根 pom.xml 的 <modules> 里加一行:
xml
<modules>
<module>ruoyi-common</module>
<module>ruoyi-framework</module>
<!-- ... 其他模块 ... -->
<module>ruoyi-crm</module> <!-- 新增 -->
</modules>
新建 ruoyi-crm/pom.xml:
xml
<parent>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId>
<version>3.3.0</version>
</parent>
<artifactId>ruoyi-crm</artifactId>
<dependencies>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-system</artifactId>
</dependency>
</dependencies>
⚠️ 关键:
ruoyi-crm不要依赖ruoyi-admin。admin 是顶层,业务模块不能反向依赖。
Step 2:迁移代码
bash
ruoyi-crm/
├── pom.xml
└── src/main/java/com/ruoyi/crm/
├── controller/ ← 从 admin 搬过来
├── service/
├── service/impl/
├── mapper/
└── domain/
包路径变了:com.ruoyi.web.controller.crm → com.ruoyi.crm.controller,IDE 批量替换即可。
Step 3:admin 加上依赖
xml
<!-- ruoyi-admin/pom.xml -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-crm</artifactId>
</dependency>
第一个坑:Spring Boot 扫描不到 Bean
代码搬过去,启动报 NoSuchBeanDefinitionException。
根因 :@MapperScan 还在 admin 里指向老路径。
解决 :把 @MapperScan 放到各模块自己内部:
less
// ruoyi-crm 模块中
@Configuration
@MapperScan("com.ruoyi.crm.mapper")
public class CrmConfig {
}
四、实战第二步:公共代码怎么不重复
拆完 CRM 和 MES 后,两个模块都需要 ExcelUtil。
错误的做法:各 copy 一份。正确做法:抽到 ruoyi-common:
bash
ruoyi-common/src/main/java/com/ruoyi/common/utils/
├── ExcelUtil.java # CRM 和 MES 都用
├── PageUtils.java # 通用分页工具
└── DictUtils.java # 字典工具
什么时候不该放 common?
CRM 和 MES 之间有一个共享 DTO------CrmCustomerBriefDTO。如果放 common,common 会逐步膨胀成大杂烩。
方案 :建一个 ruoyi-shared-dto 模块,只放跨模块共享的数据对象,common 保持干净。
common 的使命是"基础能力",不是"业务共享"。
五、实战第三步:多模块下的配置管理
每个模块不能各配各的。 Spring Boot 启动时只加载一次 application.yml,位置在 admin 的 resources 里。
模块专属配置用 @ConfigurationProperties + 前缀隔离:
less
// ruoyi-crm 模块中
@Data
@Component
@ConfigurationProperties(prefix = "crm")
public class CrmConfig {
private int importMaxRows = 5000;
private int opportunityExpireDays = 30;
}
arduino
# application.yml(只在 admin 中维护)
crm:
import-max-rows: 5000
opportunity-expire-days: 30
所有 yml 文件都放 ruoyi-admin/src/main/resources/,业务模块只定义 @ConfigurationProperties 类。
六、Maven 多模块构建与打包
版本号统一
xml
<!-- 根 pom.xml -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
<version>${ruoyi.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
子模块引用不写版本号,由根 pom 统一管控。
第二个大坑:循环依赖
CRM 依赖 MES、MES 又依赖 CRM → Circular dependency。
解法:提取接口到 common,依赖接口而非依赖实现:
kotlin
// ruoyi-common 中定义接口
public interface CustomerBriefService {
CustomerBriefDTO getBrief(Long customerId);
}
// ruoyi-crm 中实现接口
@Service
public class CustomerBriefServiceImpl implements CustomerBriefService { ... }
// ruoyi-mes 中依赖接口,不依赖模块本身
@Service
public class QualityCheckService {
@Autowired
private CustomerBriefService customerBriefService; // 注入接口
}
MES 只依赖 common 中的接口,不依赖 CRM 模块------循环依赖解了。
七、拆分后的收益
| 维度 | 拆之前 | 拆之后 |
|---|---|---|
| 启动时间 | ~40 秒 | ~18 秒 |
| 编译时间 | 改一行全量 300+ 类 | 只编译当前模块 |
| 代码冲突 | 天天 merge 冲突 | 基本不冲突 |
| 新人上手 | 要弄懂整个 admin | 读懂一个模块就能开工 |
| 删除成本 | 不敢删 | Maven 依赖图一眼看穿,敢删 |
八、总结
三条核心原则回顾:
① 依赖只向下。 底层模块永远不依赖上层。
② 公共代码提 common,业务代码放模块。 common 是基础能力集,不是垃圾桶。
③ 一次只拆一个模块。 拆一个,跑通,再拆下一个。
什么时候该拆?
- 启动时间超过 30 秒?
- 两个人以上在同一个模块里写代码?
- 你开始纠结"这个类到底该放哪个包"?
两个满足了就动手。拆分是为协作和长期维护服务的,不要为了拆而拆。
关于作者
我是一名全栈开发者,目前在深圳创业,专注于印刷包装行业的数字化系统建设。
技术栈:Java / Spring Boot / Vue3 / uni-app / MySQL / Redis
我会持续分享全栈开发实战、若依框架深度教程、MES & CRM 产品设计思路。若依框架实战系列还有:
每周更新,欢迎关注微信公众号「MqCode」,第一时间获取全栈开发实战内容 👇