soda docs v17.0.1

把 DDD 写进业务代码,
而不是写进架构图里

soda 为 Spring Boot 而生的企业级 DDD / CQRS 框架。四层架构、命令查询分离、领域事件、上下文全链路传递 —— 一个 starter 依赖就能起步。

这是什么

soda 不是一个"分层规范文档",而是一组已经在跑的代码。你引入依赖,写一个聚合根和一个 CommandHandler,框架的切面会自动把上下文带进领域事件、串起命令链路、并在递归发生时把请求掐断。

CQRS

命令与查询分开

写操作走 Command → CommandHandler → 聚合根 → 仓储,读操作走 Query → QueryHandler → 读库。两条链路各自独立演进,天然避免「一个方法又读又写」。

DDD

聚合根自己发事件

领域事件由聚合根在状态变更时产生(addPendingEvent),而不是由 Service 层手动 new。事件和状态变更天然同源。

Context

上下文全链路传递

requestId、用户身份、租户、权限随命令进入聚合根、写进领域事件、跨服务投递时一路带着,异步链路也不会断。

Safety

递归保护

同步调用有 10 层深度上限,异步事件有 20 跳 hopCount 上限。事件互相触发形成死循环时,请求会带着可读的报错停下来,而不是打满 CPU。

四层架构,边界严格

第一天就为你生成好 —— 此后一直被守住。每个文件都有唯一的家:动一处依赖,只有一个层会受影响。

Adapter入站 / 出站适配
REST ControllersAdviceDTO / VO
Application用例编排
CommandsQueriesHandlers
Domain业务核心
Aggregate RootsEntitiesDomain Events
Infrastructure技术实现细节
RepositoriesACLAssemblers

依赖方向怎么定的、每层能依赖谁,见 核心概念。

恰到好处的框架分量

DDD 需要的构件都在,多余的都没有 —— 不强加事件溯源的存储税。

与典型的 DDD 框架比一比:

对比项soda典型 DDD 框架
学习曲线● 简单●● 中等
事件溯源可扩展支持完整支持
存储效率● 高效●● 需优化
查询性能● 快速●● 需优化
调试难度● 简单●● 中等
AI 辅助开发● 内置 CLAUDE.md○ 少见

soda 的取舍很直白:读模型为查询提速,写模型守好业务规则;持久化保持克制,不默认事件溯源。落到代码里怎么写,见 写一个命令。

一次请求走完全程

这是 soda 最值得理解的一件事:一个 HTTP 请求从进来到返回,中间发生了什么。 先看全景,再看细节。

1

Controller 收到请求

你写的普通 Spring MVC Controller。ControllerAroundHandler(可选)从请求头读出 Soda-Request-Id 和 Soda-User-Name,写进 MDC 和命令对象。

2

命令进总线

busFacade.sendCommand(cmd) 把命令交给 CommandBus,总线按泛型类型找到对应的 CommandHandler Bean。

3

切面搬运上下文

CqrsAroundHandler 的 @Around 把命令里的 requestId / 用户 / 租户写进 ThreadLocal(DomainEventContext),供后面取用。

4

CommandHandler 执行领域逻辑

你的 CommandHandler 调聚合根方法,聚合根改状态并 addPendingEvent(...) 挂上领域事件 —— 事件此刻自动被注入上下文。

5

仓储保存

writeRepository.save(domain)。保存时 RepositoryEventAspect 拦截,把挂起的事件取出来发布到事件总线。

6

事件驱动下游

EventHandler(事件 Handler)收到事件做后续动作(发消息、发通知、写读库)。用 Simple 总线就是进程内,用 Redis 总线就是跨服务。

7

统一返回

CommandHandler 返回 DTO,切面兜底异常:BaseException 映射成 Result.fail(code, message),其它异常统一成「系统内部错误」,不泄漏内部细节。

完整拆解见 请求是怎么跑完的。

该从哪儿开始

5 分钟

五分钟跑通

官网生成工程 → 解压 → 直接运行。内置 H2,不用装数据库。

30 分钟

写一个命令

从聚合根到 CommandHandler 到仓储,完整走一遍写链路。

动手

工程结构

四层目录怎么摆、依赖怎么指向、每个包放什么。

深入

上下文传递

requestId 和用户身份是怎么跨线程、跨服务传下去的。

版本与坐标

本文档与 soda-core 发版同步,坐标里的版本号在构建时自动填入。最新版本以 Maven 仓库为准 —— 详见 版本与兼容。

xml
<dependency>
  <groupId>com.hibuka.soda</groupId>
  <artifactId>soda-core-base</artifactId>
  <version>17.0.1</version>
</dependency>

需要跨服务事件总线时,再加一个可选依赖:

xml
<dependency>
  <groupId>com.hibuka.soda</groupId>
  <artifactId>soda-event-redis</artifactId>
  <version>17.0.1</version>
</dependency>
提示

仓库地址:github.com/chenkangzeng1/soda-core。示例工程可以直接抄 —— 站内 工程结构 一节给了完整目录树。