沉默是金,總會發光
大家好,我是沉默
這不是一篇 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 不是銀彈。
真正的問題,是 錯把它當成教條,而不是工具。
沒有完美的架構,只有合適的架構。
-06-
粉絲福利
我這裏創建一個程序員成長&副業交流羣,
和一羣志同道合的小夥伴,一起聚焦自身發展,
可以聊:
技術成長與職業規劃,分享路線圖、面試經驗和效率工具,
探討多種副業變現路徑,從寫作課程到私活接單,
主題活動、打卡挑戰和項目組隊,讓志同道合的夥伴互幫互助、共同進步。
如果你對這個特別的羣,感興趣的,
可以加一下,微信通過後會拉你入羣,
但是任何人在羣裏打任何廣告,都會被我T掉。