只写领域模型
命令做出决定,事件演化状态,编译器在构建期把两者绑定好:没有反射,也没有基础设施代码。
快速开始里银行账户的一个命令函数,基于当前状态对一次取款做出决定:
fun onCommand(command: Withdraw): Withdrawn {
if (command.amount > state.balance) {
reject(Rejections.INSUFFICIENT_BALANCE, "The balance is ${state.balance}.")
}
return Withdrawn(command.amount)
}它的测试用同样的语言来写:给定事件,执行一条命令,期望得到事件和状态,不需要启动服务:
@Test
fun `a withdrawal the balance does not cover is rejected`() = aggregateSpec(model, id = "alice") {
given(AccountOpened("Alice"), Deposited(100))
whenCommand(Withdraw(150)) { expectRejected(Account.Rejections.INSUFFICIENT_BALANCE) }
whenCommand(Withdraw(30)) {
expectEvents(Withdrawn(30))
expectState { balance.assert().isEqualTo(70) }
}
}不用再写代码,服务就能从这个模型得到:每条命令一个 HTTP 路由、状态读取和快照查询、一份 OpenAPI 文档、一份 Manifest(wow-generator 由它生成类型化的 TypeScript 客户端),以及以 application/problem+json 返回、带错误码的拒绝。
| 你想 | 从这里开始 |
|---|---|
| 判断 Wow 是否适合你的系统 | Wow 怎样工作,然后是术语表 |
| 编写一个服务 | 快速开始,然后是决定与演化 |
| 从 TypeScript 前端调用服务 | TypeScript 客户端 |
| 在生产环境运行服务 | 身份、租户与访问,然后是订阅、失败与重放 |
| 分析数据 | 读模型,然后是工作台 |
Wow 荣获 KaiCode’26 Excellent Award。官方结果页重点提及了项目的模块化 DDD/CQRS 设计、规范且由多位评审者参与的代码审查、基于 Testcontainers 的集成测试、强制执行的测试覆盖率阈值、Detekt 静态分析、双语文档,以及长期的 Maven Central 语义化版本发布记录。