这不是一篇 DDD 教程,也不是最佳实践指南。
这是我在真实项目中尝试使用 DDD 之后的困惑、挣扎,甚至是放弃。
如果你正在考虑要不要上 DDD,
或者你已经在用,但总觉得哪里不对劲,
那这篇文章,可能会戳中你。
-01-
DDD 第一个让我崩溃的点
CQRS 分类,现实世界根本不配合
DDD + CQRS 的世界里,所有操作都应该被清晰地分成三类:
Command:只负责“改状态”,不关心返回值
Query:只读、幂等、轻量
Event:对“已经发生的事情”的被动响应
听起来很优雅,对吧?
但现实是:大量操作,三个都不是。
一个真实到不能再真实的例子
我们做了一个「服务调用系统」,用户通过 API 调用第三方服务:
POST /api/service-invoke-requests
{
"serviceId":"weather-api",
"inputData":{
"city":"Beijing"
}
}
同步返回结果:
{
"requestId":"req-123",
"status":"SUCCESS",
"outputData":{
"temperature":"25°C",
"weather":"Sunny"
}
}
现在问题来了——
这到底是 Command?Query?还是 Event?
它是 Command 吗?
Command 理论上应该只返回 id / status
但这里要返回完整的业务结果
而且它本质上是“读取用户配置 + 调用外部服务”
不符合。
那是 Query 吗?
Query 应该是只读、轻量、幂等
但这个接口可能扣费、发短信
可能执行几秒甚至更久
还是不符合。
Event?
Event 是“事情发生之后”的通知
这是用户主动发起的请求
也不是。
结果:三个都不是
你会发现,现实系统里有大量这样的操作:
操作 | 改状态 | 返回数据 | 同步 | CQRS 分类 |
|---|---|---|---|---|
用户注册 | ✅ | ❌ | ✅ | Command |
查询用户列表 | ❌ | ✅ | ✅ | Query |
调用第三方服务 | ✅ | ✅ | ✅ | ❓ |
生成报表 | ❌ | ✅ | ❌ | ❓ |
AI 生成内容 | ✅ | ✅ | ✅ | ❓ |
灰色地带,才是主流。
我的真实感受
很多操作既改状态,又要返回复杂数据
不同人对 CQRS 定义理解完全不同
Code Review 经常变成:
“这个到底算 Command 还是 Query?”
最后发现:
争论半天,对业务毫无价值。
所以我后来干脆:
// 与其纠结
CreateUserCommand
QueryUserByIdQuery
// 不如实用
CreateUserRequest
GetUserByIdRequest
不“纯粹”,但省时间、不内耗、能交付。
-02-
第二个让我放弃 DDD 的点
聚合根边界,真的太难判断
DDD 说:
聚合根是事务一致性的边界。
听起来很清晰,直到你真的开始建模。
一个让我纠结了很久的例子:收藏功能
用户收藏文章,看起来简单得不能再简单。
但你一旦开始用 DDD 思考,马上陷入泥潭:
收藏属于User?
还是属于Article?
还是一个独立的聚合根?
从不同视角看,结论完全相反
从用户角度:
收藏是“我的收藏”
用户删了,收藏要不要删?不一定
像 User 的一部分,又不完全是。
从文章角度:
收藏影响文章的收藏数
文章删了,收藏要不要删?也不一定
像 Article 的一部分,又不完全是。
从行为角度:
有 ID
有创建时间
可单独查询
看起来像独立实体。
但换个现实角度:
它不就是一张多对多中间表吗?
用 DDD 理论去“严格推导”,只会更混乱
一致性边界?
→ 收藏根本不需要强一致事务边界?
→ 收藏数完全可以异步更新生命周期?
→ 依附谁都不完全合理
最后你会发现:
是否是聚合根,取决于你打算以后怎么玩这个功能。
而不是 DDD 给你的某个“明确规则”。
我的结论
同一个功能
不同阶段
不同团队
建模方式完全不同。
而 DDD 给你的,是一堆“看起来有道理,但无法落地的判断标准”。
-03-
最致命的一点
DDD 和性能优化,是天然对立的
这是我真正放弃 DDD 的原因。
真实问题:User 表太大,必须拆表
1000 万用户
80% 查询只要 username / email
表里却有一堆 TEXT / JSON 大字段
拆表后,性能立竿见影。
但 DDD 告诉你什么?
User 是聚合根,Repository 应该返回完整 User
于是你陷入死循环:
返回完整聚合 → 性能差
按需加载 → 破坏聚合完整性
懒加载 → N+1 查询地狱
多粒度查询 → User 对象时完整、时残缺
你写的每一行代码,都在DDD 理论和性能现实之间妥协。
本质矛盾
DDD 假设:加载完整聚合不是问题
现实世界:性能才是第一约束
这个矛盾,无解。
-04-
最后一个现实问题
DDD 太花时间了
建模时间是 CRUD 的 2~3 倍
需求却随时可能被砍、被改、被推翻
团队对 DDD 理解不一致,沟通成本极高
很多时候你会发现:
还没来得及体现 DDD 的价值,需求已经变了。
-05-
总结
最后想说的几句大实话
1. 大多数项目,根本不需要完整 DDD
80% 的系统就是 CRUD
强行 DDD,只会增加复杂度
2. DDD 的思想是对的,但方法已经老了
关注业务语言
领域拆分思路
教条式建模
3. 现实中的 DDD,都是“DDD 风格”
Command 返回业务数据
查询直接写 SQL
为性能破坏聚合完整性
我们用的是DDD 的外壳,而不是 DDD 本身。
DDD 不是银弹。
真正的问题,是错把它当成教条,而不是工具。
没有完美的架构,只有合适的架构。
https://mp.weixin.qq.com/s/YurPZEv-vKRjA2LtnDmNlw