从开源DDD脚手架到自研:Spring Boot 4 + SCA 2025 + Nacos 3.1 全栈改造实战
1. 为什么我要把开源 DDD 脚手架彻底改成自己的
1.1 从“拿来就用”到“拿来就改”的转折点
大概半年前,我在做一个中台侧的业务系统重构,团队之前用的是一套开源 DDD 脚手架,结构挺完整,模块划分也像模像样,但真到落地的时候问题就来了。最直接的导火索是 Spring Boot 4 发布之后,我们想尝鲜升级,结果发现原来的脚手架依赖的 Spring Cloud Alibaba 版本还停留在 2023.x,Nacos 客户端也是 2.x 的老版本,Feign 的声明式调用在 Boot 4 的新特性下各种不兼容。那段时间我几乎每天都在跟版本冲突、配置加载顺序、自动装配报错打交道。
后来我下定决心,不再做“缝缝补补”的升级,而是直接把整套脚手架拆开,按照我们团队的实际业务场景重新组装一遍。这个过程涉及三个核心动作:第一,把 DDD 的分层结构从“形式上的分层”改成“约束上的分层”;第二,把 Spring Boot 4 和 Spring Cloud Alibaba 2025.0.0.0 的版本矩阵彻底对齐;第三,把 Nacos 3.1 作为配置中心和注册中心的上机流程完整跑通,并且把三份关键配置——bootstrap.yml、application.yml、Nacos 上的共享配置——一起改到位。
这篇文章就是记录我整个折腾过程的实操笔记。如果你也在用 DDD 脚手架,或者正准备把老项目往 Boot 4 + SCA 2025 上迁,那这篇内容应该能帮你省掉不少查文档和试错的时间。我不会只贴代码,还会把每一步“为什么这么选”的逻辑讲清楚,毕竟版本兼容这件事,光抄配置是抄不明白的。
1.2 先搞清楚:DDD 脚手架到底该“化”成什么样
很多人对 DDD 脚手架的理解停留在“目录结构好看”这个层面,觉得只要把 domain、application、infrastructure、interfaces 这几个包建出来,就算 DDD 了。我一开始也这么想,后来发现完全不是那么回事。真正的 DDD 脚手架,核心不在于包名,而在于依赖方向的控制和领域模型的隔离。
我见过太多项目,domain 层里直接引了 MyBatis 的 @Mapper,或者 application 层里塞了一堆 FeignClient 的调用。这种结构表面上分层了,实际上依赖是乱的,改一个数据库字段,整个链路都要动。所以我在改造的时候,给自己定了三条硬规矩:
domain层只允许依赖 Java 标准库和极少数基础工具包,绝对不能出现任何框架注解;application层负责编排,可以依赖domain,但不能直接依赖infrastructure的具体实现,必须通过接口;infrastructure层实现domain层定义的仓储接口,同时承担所有外部调用,包括 Feign 和 Nacos 配置读取。
这三条规矩听起来简单,但真做起来,光是拆 Feign 的调用位置就花了我两天时间。因为原来的脚手架把 Feign 客户端直接放在了 interfaces 层,导致 application 层想复用一个远程调用的时候,只能反向依赖 interfaces,整个依赖图就乱了。我把 Feign 全部下沉到 infrastructure 的 external 包下面,然后在上层定义 Gateway 接口,这样 application 层只依赖接口,具体实现由 infrastructure 提供。这个改动虽然不大,但整个项目的可测试性提升了一个档次。
2. Boot 4 配 SCA 2025.0.0.0 的版本对齐实操
2.1 版本矩阵不是随便选的,得看兼容性报告
Spring Boot 4 和 Spring Cloud Alibaba 2025.0.0.0 的搭配,不是“最新配最新”就完事了。我一开始直接上了 Boot 4.0.0 和 SCA 2025.0.0.0,结果启动的时候直接报 NoSuchMethodError,原因是 Spring Cloud 的 LoadBalancer 接口在 Boot 4 里改了签名,而 SCA 2025.0.0.0 依赖的 Spring Cloud 版本是 2025.0.0,两者之间有一个小版本的错位。
后来我查了 Spring Cloud Alibaba 的官方兼容性说明,确认了推荐的组合是:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| Spring Boot | 4.0.x | 必须用 4.0.0 及以上,但不要超过 4.0.3 |
| Spring Cloud | 2025.0.0 | 与 Boot 4.0.x 对齐 |
| Spring Cloud Alibaba | 2025.0.0.0 | 必须用这个版本,低一个版本都不行 |
| Nacos 客户端 | 3.1.0 | 由 SCA 2025.0.0.0 传递依赖 |
| Nacos 服务端 | 3.1.0 | 服务端和客户端版本尽量一致 |
| Feign | 随 Spring Cloud 2025.0.0 | 使用 spring-cloud-starter-openfeign |
这个表格是我踩了三次坑之后总结出来的。第一次是 Boot 4.0.4 配 SCA 2025.0.0.0,启动报 BeanDefinitionOverrideException;第二次是 Nacos 客户端 3.0.0 配服务端 3.1.0,配置监听偶尔失效;第三次是 Feign 的 Decoder 和 Boot 4 的 HttpMessageConverters 冲突。最后锁定在 Boot 4.0.2 + SCA 2025.0.0.0 + Nacos 3.1.0 这个组合,连续跑了三天压测,没有再出现兼容性问题。
注意:如果你用的是 Gradle,一定要在
dependencyManagement里显式引入spring-cloud-dependencies和spring-cloud-alibaba-dependencies的 BOM,否则版本会被传递依赖覆盖。
2.2 父 POM 的依赖管理怎么写才不打架
版本对齐之后,下一步就是写父 POM。我见过很多项目在父 POM 里直接引 spring-boot-starter-parent,然后在子模块里各自引 SCA 的 starter,结果就是版本冲突不断。我的做法是:父 POM 只做依赖管理,不引入任何实际依赖,所有 star