公司动态

芋道平台自定义业务模块开发实战:从DDD设计到微服务集成

📅 2026/8/13 5:19:34
芋道平台自定义业务模块开发实战:从DDD设计到微服务集成
1. 项目缘起为什么需要自定义业务模块在基于芋道Yudao这类快速开发平台进行项目迭代时我们经常会遇到一个典型场景平台内置的“系统管理”、“基础设施”等模块已经无法满足日益增长的业务需求。比如公司要上线一个新的“智能客服”功能或者开发一套独立的“供应链管理”系统。这时候如果所有代码都堆在现有模块里很快就会变得臃肿不堪维护起来像在迷宫里找路。更关键的是当需要将这个新业务功能独立部署、或者授权给其他团队使用时你会发现根本拆不出来。这就是“新建自定义业务模块”这个操作的核心价值所在。它不是一个简单的“新建文件夹”而是一种基于领域驱动设计DDD思想对复杂业务系统进行物理和逻辑隔离的工程实践。通过自定义模块你可以将特定的业务能力如订单、用户、商品封装成一个高内聚、低耦合的独立单元。这个单元拥有自己的数据模型Model、业务逻辑Service、接口层Controller以及前端页面甚至可以独立配置数据源和依赖。这样做的好处显而易见代码结构清晰团队协作边界明确功能复用和独立部署成为可能系统的可维护性和可扩展性得到质的提升。很多开发者第一次接触芋道时可能会被其丰富的内置功能所吸引认为“开箱即用”就足够了。但真正投入企业级应用开发后才会发现平台的核心价值在于其“脚手架”和“规范”能力而非那些内置功能本身。学会新建自定义业务模块意味着你从“平台使用者”转变为“平台架构者”能够真正驾驭这套框架让它为你独特的业务蓝图服务。接下来我将以一个虚拟的“知识库管理”业务为例手把手带你走通从零到一创建、配置、开发并集成一个全新业务模块的全过程并分享其中容易踩坑的细节。2. 模块化架构深度解析芋道的模块设计哲学在动手之前我们必须先理解芋道或者说其代表的技术流派如 RuoYi的模块化设计思想。这绝非简单的“分包”而是一套约定大于配置的工程结构。理解它你才能做出合理的设计避免后期返工。2.1 核心概念什么是“模块”在芋道的语境下一个“模块”Module通常对应一个独立的 Maven 模块或 Gradle 子项目。它不仅仅是一个代码包Package而是一个具备完整生命周期的工程实体。一个标准的自定义业务模块至少包含以下层次api模块定义模块对外的“契约”。主要包括DTOData Transfer Object前后端交互、服务间调用的数据传输对象。例如KnowledgeBaseCreateReqDTO创建请求、KnowledgeBaseRespDTO查询响应。VOView Object专门用于前端页面渲染的数据对象可能包含一些聚合字段。枚举Enum和常量Constant。Feign 客户端接口如果采用微服务架构。这个模块通常不包含具体实现只定义接口和数据结构供其他模块如controller或其它服务的api依赖。关键点api模块的纯净性至关重要它不应该依赖任何 Spring、MyBatis 等具体框架的注解否则会污染依赖方。biz模块或service模块这是业务模块的“大脑”和“心脏”。包含数据模型Model/Entity对应数据库表的实体类使用 JPA 注解或 MyBatis-Plus 注解定义。数据访问层Mapper/Repository数据库操作接口。业务逻辑层Service核心业务逻辑的实现处。controller层接收 HTTP 请求的入口。在芋道常见的单体架构中controller可能直接放在biz模块里在明确的前后端分离或微服务架构下controller可能会被抽离到单独的web模块中。web模块可选专用于承载controller和 Web 相关配置。在微服务架构下一个服务实例通常对应一个web模块它依赖biz和api并对外提供 HTTP 接口。数据库脚本模块对应的schema.sql表结构和data.sql初始数据存放在resources目录下。这种结构确保了“接口与实现分离”、“业务与交付分离”是构建清晰架构的基石。2.2 模块间的依赖与通信理解了模块结构还要理清它们如何协作。假设我们有一个knowledge-base知识库模块和一个user-center用户中心模块知识库需要获取创建者信息。单向依赖knowledge-base-biz模块需要依赖user-center-api模块。这样知识库业务代码里就能使用UserDTO和UserFeignClient而无需关心用户模块的具体实现。绝对禁止biz模块间相互依赖这会形成循环依赖导致项目无法编译或启动。服务间调用单体应用直接通过 Spring 容器注入对方的Service即可。但要注意这仍然要求被调用的Service接口定义在api模块中。微服务应用通过 Feign 客户端。knowledge-base-biz中引入user-center-api依赖其中定义了UserFeignClient。在knowledge-base-biz的Service实现中通过Resource注入这个 Feign 客户端进行远程调用。前端集成新建的模块通常也需要对应的前端页面。在芋道 Vue 前端项目中你需要在前端路由中注册新模块的菜单和页面组件并通过调用对应模块controller提供的 API 接口进行交互。踩坑提示API模块的版本管理当api模块被多个其他模块或服务依赖时对api的修改如增减DTO字段必须非常谨慎因为这可能导致下游调用方兼容性问题。在实际开发中我们建议为api模块建立简单的版本规范任何不兼容的修改都需升级版本号并通过项目文档或公告同步给所有依赖方。3. 实战从零新建“知识库管理”业务模块理论讲完我们进入实战环节。假设项目名称为yudao-cloud我们将创建一个名为knowledge-base的知识库管理模块。3.1 第一步后端模块创建与工程结构搭建首先在后端项目根目录下创建模块文件夹。通常芋道项目已经有一个清晰的父POM管理所有子模块。创建模块目录在项目根目录的pom.xml同级新建文件夹yudao-module-knowledge-base。创建子模块在yudao-module-knowledge-base文件夹内分别创建knowledge-base-api、knowledge-base-biz两个子模块文件夹。配置父POM打开项目根pom.xml在modules节点下添加新建的模块。modules moduleyudao-module-system/module moduleyudao-module-infra/module !-- 新增自定义模块 -- moduleyudao-module-knowledge-base/module /modules编写各模块的pom.xmlknowledge-base-api/pom.xml: 此模块应尽可能“轻”只引入必要的依赖如lombok、jakarta.validation-api参数校验以及项目内部定义的通用工具包yudao-common。dependencies !-- 项目内通用依赖 -- dependency groupIdcn.iocoder.cloud/groupId artifactIdyudao-common/artifactId /dependency !-- 参数校验 -- dependency groupIdjakarta.validation/groupId artifactIdjakarta.validation-api/artifactId /dependency /dependenciesknowledge-base-biz/pom.xml: 此模块是核心实现需要引入大量依赖。dependencies !-- 依赖自己定义的api -- dependency groupIdcn.iocoder.cloud/groupId artifactIdknowledge-base-api/artifactId version${revision}/version /dependency !-- Spring Boot Starter -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 数据访问 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId /dependency !-- 数据库驱动 (以MySQL为例) -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- 连接池 -- dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot-starter/artifactId /dependency !-- 如果需要调用其他服务 -- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-openfeign/artifactId /dependency /dependencies创建启动类与配置在knowledge-base-biz的src/main/java下创建包cn.iocoder.cloud.module.knowledge并新建一个KnowledgeBaseServerApplication启动类。关键点使用SpringBootApplication注解并通过MapperScan指定 Mapper 接口的扫描路径。SpringBootApplication MapperScan(cn.iocoder.cloud.module.knowledge.dal.mysql.mapper) // 注意扫描路径 EnableFeignClients(basePackages cn.iocoder.cloud) // 如果需要Feign public class KnowledgeBaseServerApplication { public static void main(String[] args) { SpringApplication.run(KnowledgeBaseServerApplication.class, args); } }创建配置文件在knowledge-base-biz/src/main/resources下创建application.yaml和application-{env}.yaml。这里需要特别注意自定义模块的配置如何与主配置协同。通常我们会在主应用的application.yaml中通过spring.profiles.include来激活特定环境的模块配置。但更清晰的做法是在自定义模块的配置中只配置本模块特有的属性如数据源如果独立、MyBatis Mapper 位置等。通用的 Redis、RabbitMQ 配置应放在主配置或基础设施模块中。3.2 第二步定义数据模型与API契约这是体现业务设计的核心步骤。设计数据库表与实体Model在knowledge-base-biz模块的dal/dataobject包下创建实体类KnowledgeBaseDO。TableName(knowledge_base) Data EqualsAndHashCode(callSuper true) Builder NoArgsConstructor AllArgsConstructor public class KnowledgeBaseDO extends BaseDO { TableId(type IdType.AUTO) private Long id; private String title; private String content; private Long categoryId; private Integer viewCount; private Integer status; }定义API接口与DTO在knowledge-base-api模块中创建dto包。KnowledgeBaseCreateReqDTO: 用于创建请求包含NotBlank等校验注解。KnowledgeBaseUpdateReqDTO: 用于更新请求。KnowledgeBaseRespDTO: 用于查询响应可以比实体类包含更多关联信息如分类名称。KnowledgeBasePageReqDTO: 用于分页查询请求继承平台通用的PageParam。如果需要对外提供 RPC 接口还需创建KnowledgeBaseFeignClient接口并使用FeignClient注解。3.3 第三步实现业务逻辑与数据访问创建 Mapper 接口与 XML在knowledge-base-biz的dal/mysql/mapper包下创建KnowledgeBaseMapper接口并编写对应的KnowledgeBaseMapper.xml文件。使用 MyBatis-Plus 可以极大简化单表操作。创建 Service 接口与实现在service包下创建KnowledgeBaseService接口定义业务方法。在service/impl包下创建KnowledgeBaseServiceImpl实现类。这里实现具体的增删改查、状态变更等逻辑。重要实践复杂的业务逻辑特别是涉及多个实体操作或远程调用的务必使用Transactional注解保证事务一致性并考虑异常处理与回滚。创建 Controller在controller包下创建KnowledgeBaseController。这里的核心是调用Service并遵循 RESTful 风格设计 API 路径。务必做好参数校验可使用 Spring Validation和统一的响应体封装。3.4 第四步数据库脚本与前端集成编写SQL脚本在knowledge-base-biz/src/main/resources/sql目录下创建schema.sql和data.sql。在项目启动或通过 Flyway/Liquibase 执行。脚本中应包含建表语句和必要的初始数据。前端菜单与路由配置在芋道 Vue 前端项目的src/router/modules/目录下新建一个knowledgeBase.js路由文件定义知识库模块的菜单路由。在src/api/目录下新建knowledgeBase.js文件使用 Axios 定义调用后端KnowledgeBaseController接口的方法。在src/views/目录下创建对应的 Vue 页面组件并在路由文件中关联。最后需要在主路由文件或菜单管理后台将新模块的路由动态添加到系统中。核心避坑点模块的独立性与配置隔离很多新手在创建模块后启动主应用发现新模块的 Controller 没被扫描到或者配置文件不生效。根本原因在于 Spring Boot 的组件扫描机制。你需要确保主应用的SpringBootApplication注解能扫描到自定义模块的包。通常主应用在顶层包如cn.iocoder.cloud自定义模块在其子包下如cn.iocoder.cloud.module.knowledge这样是默认能扫描到的。如果模块包名不在主应用扫描范围内需要在主应用启动类显式添加ComponentScan。自定义模块的配置文件application.yaml必须被正确加载。最稳妥的方式是在主应用的application.yaml中通过spring.config.import显式导入spring.config.importoptional:classpath:knowledge-base-biz/application.yaml。这样可以确保模块配置的优先级和隔离性。4. 进阶配置与深度集成让模块真正“活”起来模块创建并跑通基础CRUD只是第一步。要让它成为企业级应用的一部分还需要解决一系列集成问题。4.1 多数据源配置如果你的自定义模块业务数据量很大或者出于隔离性考虑希望使用独立的数据库就需要配置多数据源。在模块配置中定义数据源属性在knowledge-base-biz的application.yaml中定义专属的数据源。spring: datasource: knowledge-base: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/knowledge_base?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai username: root password: 123456创建数据源配置类在模块中创建一个Configuration类使用ConfigurationProperties绑定上述属性并生成一个DataSourceBean同时配置对应的SqlSessionFactory和TransactionManager。关键给这个数据源和事务管理器起一个唯一的名字Qualifier如knowledgeBaseDataSource。在 Mapper 接口上指定数据源在自定义模块的 Mapper 接口上使用DS(“knowledge-base”)注解如果使用 dynamic-datasource 组件或在SqlSessionFactory配置中指定以告知 MyBatis 使用哪个数据源。4.2 权限系统集成芋道平台通常有完善的权限管理系统基于角色或数据权限。自定义模块必须无缝集成进去。声明权限标识符在knowledge-base-api模块中定义一个权限常量类如KnowledgeBasePermissions。public class KnowledgeBasePermissions { public static final String KNOWLEDGE_BASE_CREATE “knowledge:base:create”; public static final String KNOWLEDGE_BASE_UPDATE “knowledge:base:update”; public static final String KNOWLEDGE_BASE_DELETE “knowledge:base:delete”; public static final String KNOWLEDGE_BASE_QUERY “knowledge:base:query”; }在 Controller 方法上添加注解在KnowledgeBaseController的方法上使用芋道平台提供的权限注解如PreAuthorize或自定义的RequiresPermissions进行声明。PostMapping(“/create”) RequiresPermissions(KnowledgeBasePermissions.KNOWLEDGE_BASE_CREATE) public CommonResultLong createKnowledgeBase(Valid RequestBody KnowledgeBaseCreateReqDTO reqDTO) { // ... }同步权限到数据库平台启动时需要有机制如监听ApplicationReadyEvent事件将这些权限标识符扫描并持久化到系统的权限表中。通常平台会提供相关的 Service 接口来完成此操作。前端按钮权限控制前端页面中按钮的显示隐藏需要与这些权限标识符绑定。芋道前端框架一般提供了v-permission之类的指令来实现。4.3 消息队列与分布式事务对于涉及多个模块或服务的复杂操作需要考虑异步和解耦。定义领域事件当知识库文章被发布时可能触发“文章已发布”事件通知搜索模块建立索引。在api模块中定义事件类KnowledgeBasePublishedEvent。发布事件在KnowledgeBaseServiceImpl的发布方法中使用ApplicationEventPublisher发布该领域事件。监听与处理在搜索服务或其他相关模块中使用EventListener或TransactionalEventListener监听该事件并执行建索引等操作。对于跨服务的场景则需要引入消息中间件如 RocketMQ、Kafka将事件转换为消息进行可靠投递。分布式事务如果“发布文章”和“建立索引”需要保证一致性就需要考虑分布式事务方案如 Seata 的 AT 模式或基于消息的最终一致性方案本地消息表。4.4 模块的打包与部署最后模块如何交付单体部署最简单所有模块打包成一个jar/war一起部署。只需确保主应用pom.xml依赖了自定义模块的biz模块。微服务部署需要将knowledge-base-biz连同内嵌的web层打包成一个独立的 Spring Boot 应用jar包。此时knowledge-base-api模块会被其他服务依赖以进行 Feign 调用。你需要为这个独立服务配置独立的端口、注册中心Nacos、配置中心等。Docker 化为独立部署的模块服务编写Dockerfile基于 JDK 镜像构建应用镜像并通过环境变量或配置中心管理配置。5. 常见问题排查与效能提升技巧在实际操作中你一定会遇到各种“坑”。这里汇总几个高频问题及其解决方案。问题一模块启动失败报BeanDefinitionNotFoundException或No qualifying bean。排查思路检查包扫描确认主应用启动类SpringBootApplication的扫描范围是否包含了自定义模块的所有组件Component,Service,Controller等。最直接的方法是检查自定义模块的包名是否在主应用类所在包或其子包下。如果不是需要在主应用启动类添加ComponentScan(basePackages {“cn.iocoder.cloud”})明确指定。检查依赖传递确认自定义模块的biz模块是否被主应用模块正确依赖。检查主应用的pom.xml中是否引入了knowledge-base-biz。检查配置类如果模块中有自定义的Configuration配置类如数据源、RedisTemplate等确保该类被ComponentScan扫描到或者使用Import注解在主配置中显式导入。问题二自定义模块的配置文件不生效无法读取application-knowledgebase.yaml中的属性。解决方案Profile激活确保启动时激活了对应的 Profile。例如在application.yaml中设置spring.profiles.active: dev,knowledgebase。显式导入推荐在application.yaml中使用spring.config.import属性这是 Spring Boot 2.4 推荐的方式优先级和隔离性更好。spring: config: import: - optional:classpath:application-knowledgebase.yaml属性覆盖理解 Spring Boot 的属性加载顺序。jar包外部的application.yaml会覆盖jar包内部的。确保你的外部配置文件位置正确。问题三前端页面能打开但调用后端 API 返回 404 或 500。排查链路检查 Controller 路径确认前端调用的 URL 路径与后端RequestMapping定义的路径完全匹配包括上下文路径server.servlet.context-path。检查接口权限如果接口有RequiresPermissions等权限注解而当前登录用户没有该权限会返回 403。检查用户角色和权限分配。查看后端日志这是最直接的。在 IDE 控制台或日志文件中查找ERROR或WARN级别的日志通常会有详细的异常堆栈信息。常见原因有参数校验失败Valid、数据库查询异常、空指针等。使用 API 测试工具在开发阶段强烈建议使用 Postman 或 Swagger UI 直接测试后端接口排除前端代码问题。效能提升技巧代码生成器的活用芋道平台通常配套了强大的代码生成器。在定义好数据库表后可以利用代码生成器一键生成Entity、Mapper、Service、Controller乃至前端 Vue 页面代码。这能节省大量重复劳动。但切记生成的代码是“骨架”复杂的业务逻辑仍需手动填充和优化。建立模块模板当你需要创建第二个、第三个自定义模块时会发现很多步骤是重复的。可以建立一个“模块模板”项目包含标准的pom.xml、目录结构、通用的配置类和工具类。新模块直接复制此模板进行修改效率倍增。接口文档自动化在Controller和DTO上使用 SwaggerApi,ApiOperation,ApiModelProperty注解。集成 Knife4j 等增强 UI可以自动生成美观的接口文档极大方便前后端联调和后续维护。整个过程下来新建一个自定义业务模块确实涉及不少步骤但从架构整洁度和长期维护成本来看这些投入是绝对值得的。关键在于理解其设计理念并形成自己团队的标准操作流程。当你熟练之后创建一个结构清晰、功能完备的新模块可能只需要喝杯咖啡的时间。