MateCloud DDD四层架构实战跟着一个请求看清每一层的边界【免费下载链接】matecloudMateCloud是一款基于Spring Cloud Alibaba的微服务架构。目前已经整合Spring Boot 4.0.7、 SpringCloud 2025、Spring Cloud Alibaba 2025、Spring Security Oauth2、Feign、Dubbo、JetCache、RocketMQ等支持多租户的低代码平台Saas平台开发套件项目地址: https://gitcode.com/GitHub_Trending/ma/matecloud在用户模块加一个字段要动6个文件、800行的UserService、单测要先mock半个类——这些痛点的解药就是DDD四层架构。MateCloud是一个基于Spring Cloud Alibaba的微服务项目整合Spring Boot、Dubbo、RocketMQ等面向多租户SaaS开发把这套分层落成了真实源码Trigger、Application、Domain、Infrastructure四层各管各的严禁越界。本文以它真实的用户模块为例一步步拆解这次微服务分层架构实践。 一分钟看懂核心思想每层只管自己的事思路不复杂把业务规则和技术实现分开把用例编排和入口协议分开。每个服务模块下就四个目录Trigger层trigger/管外部入口——REST控制器、Dubbo RPC、事件监听只做认证、参数校验、结果包装严禁写业务规则Application层application/管用例编排——CQRS把读写拆成两条路写命令服务管事务边界严禁自己揣业务规则Domain层domain/业务核心——聚合根、实体、值对象、领域服务所有业务不变量在这严禁引用Spring、MyBatis注解Infrastructure层infrastructure/管技术实现——DAO、PO、仓储实现、事件发布实现严禁让业务概念往上泄漏只给领域层定义的接口做适配器。关键在依赖方向领域层不依赖任何人。仓储接口由领域层定义、基础设施层实现这是依赖倒置也是可替换的前提。admin角色/菜单/字典和tenant租户子模块用的是同一套结构说明这是全项目约定不是某个模块的偏好。 跟着一个请求走创建用户的完整链路以创建一个用户POST /api/v1/users为例从入口追到数据库。① Trigger层认证、校验、然后交给下一层请求经网关转发到mate-system落在 UserController。类级SaCheckLogin查登录方法级SaCheckPermission(Perms.USER_ADD)查权限Valid完成参数校验然后直接转交命令服务。如果在这里写用户名查重密码编码后面新增RPC或导入入口时这些规则就得再实现一遍——这正是它不碰业务的原因。② Application层事务边界与编排写路径的核心是 UserCommandService 的 createUser它演示了应用层的分工Transactional(rollbackFor Exception.class) public String createUser(RegisterUserCommand command) { tenantQuotaService.assertCanAddUser(TenantContext.getTenantId()); UserAggregate aggregate userDomainService.createUser( command.getUsername(), command.getPassword(), command.getMobile(), command.getEmail(), command.getRealName()); userRepository.save(aggregate); eventPublisher.publish(new UserCreatedEvent( aggregate.getId(), command.getUsername(), command.getMobile())); return aggregate.getId(); }先看多租户的套餐配额再让领域服务创建聚合经仓储接口持久化最后发一个领域事件——监听器必须用TransactionalEventListener(AFTER_COMMIT)回滚时不会有幽灵事件。关键整个方法没有一条if/else业务规则只有先A后B、事务我来管。③ Domain层业务规则与状态变更UserDomainServiceImpl 是一个没有任何Spring注解的类它做校验用户名查重、手机号查重、密码经PasswordEncoderPort端口哈希领域层不关心用哪种加密然后UserAggregate.createNew生成聚合——分配UUID、状态置为ACTIVE并记录一条CREATE操作流水。如果这些校验写在SQL或DAO里规则就只存在于那一处下一个人走导入或RPC创建用户时就直接绕过去了。④ Infrastructure层把聚合落进数据库UserRepositoryImpl 接手领域层定义的仓储接口转换器把实体转成UserPOMyBatis-Plus的UserDao插入再把聚合上累积的操作流水批量入库。表结构、mapper、逻辑删除TableLogic这些技术细节全封在这层哪天换持久化框架动的只有这个目录。读路径更短UserQueryServiceImpl 经仓储接口取聚合再用转换器 UserConvertor 返回UserInfoResponse——这个DTO定义在mate-api模块其他服务的RPC调用共用。同一套领域代码还被 RpcUserServiceImpl 和Excel导入复用importBatch 就是逐行走同一个 createUser 管线保证两个入口规则一致。 分层要点速览Trigger层协议适配器业务逻辑出门口即停职责边界HTTP/RPC/事件的最后一段路只做认证、校验和Result包装。PostMapping SaCheckPermission(Perms.USER_ADD) public ResultString createUser(Valid RequestBody RegisterUserCommand command) { return Result.ok(userCommandService.createUser(command)); }关键在转手就走控制器代码量几乎是恒定的它变长就是逻辑泄漏的信号。常见误区把查重这类规则放控制器里后果是每个新入口都要复制一份。Application层CQRS拆读写事务在这层职责边界写走CommandService挂Transactional读走QueryService返回DTO。Override public UserInfoResponse findById(String userId) { UserAggregate aggregate userRepository.findById(userId); return aggregate null ? null : userConvertor.toResponse(aggregate); }关键读路径绝不碰改状态的方法返回的是DTO而不是聚合根。常见误区把状态机if/else塞进应用层规则就和这条编排绑死了别的用例没法复用。Domain层纯Java一个框架注解都没有职责边界聚合、实体、值对象、领域服务所有业务不变量在这层。public void freeze() { checkActive(); this.status UserStatus.FROZEN; } private void checkActive() { if (isFrozen()) throw UserException.of(UserErrorCode.USER_IS_FROZEN); if (isDomainDeleted()) throw UserException.of(UserErrorCode.USER_IS_DELETED); }关键已删除的用户不能再被冻结写在实体自己的方法里谁来调都绕不过。常见误区给领域类加Service或MyBatis注解领域层从此没法用纯JUnit测。Infrastructure层只为领域定义的接口做实现职责边界DAO、PO、仓储实现、事件发布实现只有技术细节。Override Transactional(rollbackFor Exception.class) public void save(UserAggregate aggregate) { UserPO po UserInfraConvertor.INSTANCE.toPO(aggregate.getUser()); userDao.insert(po); aggregate.getUser().setId(po.getId()); batchSaveStreams(aggregate.getAndClearOperateStreams()); }关键转换、表结构、逻辑删除都封在里面领域层从头到尾没看见UserPO。常见误区把仓储接口放在infra、让domain依赖它依赖方向一反过来可替换就是空话。 传统写法 vs DDD四层一笔账怎么算维度传统写法大ServiceMateCloud的四层改一个字段/规则Controller/Service/DAO/mapper多处散改领域模型转换类入口和其他层不动单测业务规则要Spring容器测试库慢纯JUnit不起容器见 UserTest、UserAggregateTest换持久化技术业务代码与DAO缠绕只动infra层的Dao与仓储实现新增入口RPC/导入/事件逻辑复制或跨层乱调复用同一个Command服务多租户隔离每条SQL手工加租户条件命令层套餐配额断言租户拦截器自动圈定查询范围⚠️ 分层架构最常见的坑业务逻辑写进Controller。后果规则只存在于那一处单测只能走HTTP集成。正确姿势控制器只做认证、校验、转交规则下沉领域层。领域层引入框架注解。Service、Transactional、MyBatis注解出现一个领域层就不可移植了。正确姿势看 UserDomainServiceImpl——零注解Bean由基础设施层的 DomainServiceConfiguration 注册。应用层直调DAO绕过领域。后果冻结用户不能改资料这类不变量只在部分入口生效同一份数据不同入口行为不一致。正确姿势写操作一律 领域服务 → 聚合 → 仓储。聚合根直接返回给前端。后果密码这类内部字段可能泄漏表结构一变前端跟着遭殃。正确姿势读路径统一经转换器出DTO。什么都上全套四层。单表字典CRUD也切四层样板代码比逻辑还多。我的建议按领域复杂度决定建模深度值得拆的才拆简单域可以先用两层过渡。 落地清单在你自己项目复刻的最小步骤先选一个限界上下文挑你最熟的模块用户、订单、角色都行划出trigger/application/domain/infrastructure四个目录先不动代码只搭骨架再抽领域模型把聚合根、实体、值对象从Service里挪出来什么状态下能做什么变成实体方法只留纯POJO和接口然后做依赖倒置仓储接口放domain、实现放infrastructure领域服务的Bean用config类注册再拆读写Command/Query分开读走转换器出DTO统一Result返回最后补入口RPC、事件监听、导入都进trigger层复用同一套命令服务。设计动机和约定细节项目里都有现成文档可以对照。 收尾它解决了什么没解决什么回到开头的问题字段变更不再散落六处文件规则变更集中在领域模型业务规则的单测不用容器就能写。但四层架构不解决所有事——限界上下文的边界怎么划、建模做到多深仍然靠你的判断前期建模成本比一个大Service更高很小的CRUD项目硬上全套反而负担。这套架构是前期付费、后续每次需求变更都在回本。想深入设计动机直接看 DDD架构RFC、编码规范 和 用户模块源码。【免费下载链接】matecloudMateCloud是一款基于Spring Cloud Alibaba的微服务架构。目前已经整合Spring Boot 4.0.7、 SpringCloud 2025、Spring Cloud Alibaba 2025、Spring Security Oauth2、Feign、Dubbo、JetCache、RocketMQ等支持多租户的低代码平台Saas平台开发套件项目地址: https://gitcode.com/GitHub_Trending/ma/matecloud创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考