架构设计
为什么把业务模块做成「应用」
Wujies 团队
单体后台在「一套系统交付 N 个客户」时最痛:功能组合不同、加模块要停机、升级要整体重启。这篇讲我们为什么把业务模块改成可安装的应用,以及为此付出了什么。
上游 RuoYi-Vue-Plus 的工作方式是单体结构:所有业务模块在同一个 reactor 里编译、打进同一个 jar。一套系统服务一个客户时,这没问题;但当我们开始把同一套系统交付给多个客户,问题就集中出现了。
三个绕不开的痛点
- 客户 A 要商城,客户 B 要考试,客户 C 两个都要。单体结构下,要么带上全部模块,要么给每个客户维护一个分支。
- 客户临时要加一个模块,流程是:改代码 → 重新编译 → 停机发版。
- 某个模块升级,整个服务重启,影响其它所有模块。
这三个问题的共同点是:业务模块的生命周期被绑死在了宿主上。我们要做的,是把「平台」和「业务」的生命周期拆开。
应用化之后
每个应用是插件仓库里一个自包含工程:实体、Mapper、Service、Controller、Mapper XML、迁移脚本、app.json、前端页面,全在它自己的目录下。平台对 cn.wujies.<app>.* 的 Java 引用为 0。
wujies-plugins/wujies-exam/
├── app.json 编码、版本、菜单、产物路径
├── migrations/ 建表与种子数据
├── src/main/java/cn/wujies/exam/
└── frontend/exam-app/ 独立 ESM 前端产物安装一个应用会同时带来四件事:后端 jar、数据库迁移、菜单与权限、前端远程页面。四件事是一套的,缺一件就会表现成「装了但用不了」。
代价是什么
插件化不是免费的。为了做到运行期装卸,我们引入了独立类加载器、子 Spring 上下文、插件依赖的 provided 约定、迁移编排、包白名单与签名校验,以及前端远程产物的 import map 机制。
这些机制对「一套系统服务一个客户」的项目是纯成本。所以我们在文档里明确写了什么情况下不要用这套结构:功能固定、没有分发诉求、不打算做应用市场,直接用上游更省事。
判断标准
应用化真正买到的是分发与隔离,不是「代码更整洁」。
如果这句话正好说中你的场景,继续读「插件开发」;如果只是想把代码分模块,普通多模块工程就够了。