芋道源码架构解析:从模块化设计到多租户实现

张开发
2026/4/11 8:05:03 15 分钟阅读

分享文章

芋道源码架构解析:从模块化设计到多租户实现
1. 芋道源码项目概览第一次接触芋道源码时我正为一个电商项目寻找合适的后台管理系统。当时被它丰富的功能模块吸引——从基础的RBAC权限控制到多租户支持从工作流引擎到AI集成几乎覆盖了企业级应用的所有常见需求。但真正让我决定深入研究的是它清晰的模块化架构设计。芋道源码原名Ruoyi-vue-pro是一个基于Spring Boot的单体架构Java后台管理系统。它采用经典的多模块Maven项目结构将不同功能划分为独立模块通过严谨的依赖管理实现高内聚低耦合。这种设计让项目在保持功能丰富性的同时依然具备良好的可维护性和扩展性。在实际项目中我特别欣赏它的三个设计特点模块化分层将框架组件与业务代码彻底分离依赖倒置通过API模块解耦业务模块间的调用多租户实现优雅地处理了数据隔离问题2. 模块化架构设计解析2.1 根模块的智慧项目的根pom.xml文件是整个架构的基石。我曾在一次项目升级时深刻体会到它的价值——当需要统一升级Spring Boot版本时只需修改根pom中的一处配置所有子模块自动同步更新。!-- 根pom.xml片段 -- properties revision2.4.0-jdk8-SNAPSHOT/revision spring.boot.version2.7.18/spring.boot.version /properties dependencyManagement dependencies dependency groupIdcn.iocoder.boot/groupId artifactIdyudao-dependencies/artifactId version${revision}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement这种设计解决了企业级项目常见的版本冲突问题。我曾见过一个项目因为不同模块引用了不同版本的Fastjson导致线上出现诡异的JSON解析错误。芋道的依赖管理机制从根本上杜绝了这类问题。2.2 模块分类与职责芋道的模块主要分为四类框架模块(yudao-framework)包含各种技术组件的封装比如yudao-spring-boot-starter-mybatisMyBatis集成yudao-spring-boot-starter-redisRedis客户端yudao-spring-boot-starter-biz-tenant多租户支持业务模块(yudao-module-xxx)每个业务功能独立成模块如yudao-module-system系统管理yudao-module-infra基础设施依赖管理模块(yudao-dependencies)统一管理第三方依赖版本启动模块(yudao-server)聚合各业务模块提供启动入口这种分类方式我在实际项目中验证过其价值。当需要为某个客户定制CRM功能时我们只需专注修改yudao-module-crm模块完全不用担心会影响其他功能。3. 多租户实现原理3.1 多租户的三种模式芋道支持三种多租户模式我在不同场景下都实践过独立数据库每个租户使用完全独立的数据库实例共享数据库独立Schema同一数据库实例不同租户使用不同Schema共享数据库共享Schema通过字段区分租户数据项目中通过TenantProperties类配置模式选择ConfigurationProperties(prefix yudao.tenant) public class TenantProperties { /** * 租户模式 */ private Boolean enable true; private TenantMode mode TenantMode.COLUMN; public enum TenantMode { DATABASE, SCHEMA, COLUMN } }3.2 数据隔离实现细节最常用的是COLUMN模式其核心在于动态SQL改写。芋道通过MyBatis插件实现public class TenantDatabaseInterceptor implements InnerInterceptor { Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 获取当前租户ID Long tenantId TenantContextHolder.getRequiredTenantId(); // 解析原始SQL String originalSql boundSql.getSql(); // 对SELECT/INSERT/UPDATE/DELETE语句添加租户条件 String newSql TenantSqlParser.process(originalSql, tenantId); // 重置BoundSql resetBoundSql(ms, boundSql, newSql); } }这个设计有个精妙之处通过TenantIgnore注解可以豁免特定方法的多租户过滤。比如在统计报表场景下我们需要跨租户查询数据TenantIgnore public ListOrder getAllOrders() { return orderMapper.selectList(); }3.3 租户上下文传递跨服务调用时租户信息的传递是关键。芋道通过两种方式保证上下文不丢失HTTP请求通过TenantRequestInterceptor拦截器自动从请求头中提取tenant-id异步任务通过TenantJobDecorator装饰器在执行前设置租户上下文public class TenantRequestInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String tenantId request.getHeader(tenant-id); if (StringUtils.isNotBlank(tenantId)) { TenantContextHolder.setTenantId(Long.valueOf(tenantId)); } return true; } }4. 业务模块设计精要4.1 API与BIZ分离模式芋道每个业务模块都拆分为api和biz两个子模块这个设计解决了我在分布式架构中常遇到的循环依赖问题。api模块定义接口、DTO、枚举等biz模块实现具体业务逻辑当模块A需要调用模块B时只需依赖B的api模块!-- 模块A的pom.xml -- dependency groupIdcn.iocoder.boot/groupId artifactIdyudao-module-b-api/artifactId version${revision}/version /dependency这种设计带来了三个好处避免模块间直接依赖实现类API变更影响范围清晰可控为未来微服务化打下基础4.2 标准化的包结构每个业务模块都遵循相同的包结构规范这让代码就像乐高积木一样可以自由组合。以系统管理模块为例yudao-module-system-biz ├── src │ ├── main │ │ ├── java │ │ │ └── cn.iocoder.yudao.module.system │ │ │ ├── controller │ │ │ ├── service │ │ │ ├── dal │ │ │ └── convert │ │ └── resources │ │ └── mapper特别值得一提的是convert包它使用MapStruct实现DTO/DO/VO之间的转换Mapper public interface UserConvert { UserConvert INSTANCE Mappers.getMapper(UserConvert.class); Mapping(source deptId, target dept.id) UserDO convert(UserCreateReqVO bean); Mapping(source dept.id, target deptId) UserRespVO convert(UserDO bean); }这种规范化的结构大大降低了新成员的熟悉成本。我记得有个新同事入职第二天就能独立开发新功能这要归功于这种清晰的结构设计。5. 实战中的架构演进5.1 从单体到微服务的平滑过渡芋道的模块化设计为架构演进留足了空间。我们曾将一个大型项目从单体逐步拆分为微服务整个过程异常顺利第一阶段将yudao-module-crm模块单独打包部署第二阶段通过Dubbo将API接口暴露为服务第三阶段为CRM服务单独配置数据库关键点在于flatten-maven-plugin插件的使用它解决了模块独立部署时的版本问题plugin groupIdorg.codehaus.mojo/groupId artifactIdflatten-maven-plugin/artifactId configuration flattenModeoss/flattenMode updatePomFiletrue/updatePomFile /configuration /plugin5.2 多租户性能优化在高并发场景下我们发现多租户的SQL改写会成为性能瓶颈。通过以下优化手段QPS提升了3倍缓存SQL解析结果对相同SQL模板只解析一次批量操作优化对批量INSERT等操作进行特殊处理租户白名单配置不需要过滤的表public class TenantSqlParser { private static final CacheString, String SQL_CACHE Caffeine.newBuilder().maximumSize(1000).build(); public static String process(String sql, Long tenantId) { return SQL_CACHE.get(sql tenantId, k - doParse(sql, tenantId)); } }这些优化经验让我深刻体会到好的架构不仅要设计优雅更要经得起性能考验。

更多文章