对DDD的常见误区
這里是Z哥的個人公眾號
每周五11:45 按時送達
當然了,也會時不時加個餐~
我的第「205」篇原創敬上
大家好,我是Z哥。
我從 2014 年開始接觸 DDD 到現在也有 7 年多時間了,在這個期間踩過很多坑,也是自己慢慢從充滿疑問,然后一步一步摸索著到如今可以輕松落地 DDD 的地步。
在 2014 年那會,DDD 的社區遠不如現在那么繁榮,甚至還有很多鄙視它的聲音。因此網上的資料也很少,大部分的知識獲取源自兩本書《領域驅動設計 軟件核心復雜性應對之道》和《實現領域驅動設計》。
所以,我的 DDD 之路和現在的很多人可能不太一樣,我是沿著一個書本上濃縮后的核心思想,然后自下而上地通過自己的摸滾打爬慢慢構建的知識體系,而現在很多人是照著一些課程中教的偏實踐型的知識,自上而下的模仿,然后再根據自己的理解和場景做出調整優化。
最近幾年 DDD 一直很火,網上的課程也有很多。但是說實話,DDD 里面的大多數概念還是很晦澀的,沒有那種特別清晰規則,可以像代碼規范那樣明確指導我們如何 coding,什么是好代碼,什么是壞代碼。
很多小伙伴從我之前在博客園寫的 DDD 系列找過來,和我交流 DDD 相關的問題,發現大家對其中的不少概念還存在一些疑惑。或者說,有些概念自己覺得清楚了,但是落地的時候好像又不知道從何下手。
今天我們就來聊聊我收集到的一些大家的困惑,來和大家交流一下我的觀點。
/01 ?DDD 必須要配合微服務一起用?/
這個問題還有另一種問法,“單體應用能用 DDD 嗎?”。
先說結論,必須可以,不是微服務也可以用。
我覺得大家之所以形成這個誤區背后的邏輯是這樣的。因為 DDD 提倡構建領域模型,劃定限界上下文,而限界上下文在微服務中恰好能體現為一個單獨的Service,看上去是如此的天衣無縫,它們倆是一組黃金搭檔,就該一起使用。
其實,在我看來這兩者之間的關系并不是搭檔關系,而是一種“道”和“術”的關系。DDD 是一種架構設計方法論,而微服務是一種具體的架構設計實踐方案。前者是“道”,后者是“術”,符合某一個“道”的“術”從來都不只有一個。
可能你要問了,單體應用怎么用 DDD ?建議你可以嘗試用模塊的概念來劃定限界上下文,并且將不同的模塊分別獨立一個包,以此達到類似 Service 的隔離效果。剩下的建模工作就沒什么不同了。
/02 ?限界上下文只能通過拆分成獨立的 Service 體現?/
這個問題其實在第一點里已經解答了,不用拆分成獨立的 Service 也可以體現限界上下文。
如果你存在這個問題的困惑,大概率是你在平時的開發中沒有帶著「上下文」這個概念去 coding。
因為我們設計的每一個模型,其實都是對現實世界的抽象,而現實世界中的每一個概念都有它所對應的上下文,脫離上下文任何一個詞都有歧義。哪怕是相同的一個詞在不同的上下文里表達的含義可能不同,比如,售票系統的中的“座位”與設計電影院的規劃系統里的“座位”并不是同一個東西。
/03 ?業務簡單的項目不適合 DDD ?/
這個問題其實就類似于,面積小的房子做裝修是不是不需要精心設計?只有那些高大上的別墅、甚至是大型場館才需要精心設計?
我想答案是顯而易見的。
不管是簡單的項目還是復雜的項目,他們都不影響良好的抽象設計給項目帶來的好處。
不過有些人可能糾結的點在于與 DDD 配套的技術框架、基礎設施太復雜。我覺得這個想法就有點本末倒置了。不管是什么技術框架、組件,本身只是一種工具。你拿微服務的那套放到一個簡單的單體應用里自然會覺得復雜。但是我們相比回到使用傳統的三層架構,更應該是要找到,甚至是自己打造更合適的工具。
/04 ?Entity 的屬性字段需要對應數據表嗎?/
我認為這個問題的答案首先有一點是確定的,就是:不像三層架構那樣,完全一一對應。
因為 Entity 里面可能會嵌套其它的 Entity,以及 ValueObject。所以里面的屬性數量大多會比數據表的字段更多。
另外,我覺得這里最關鍵的問題是,Entity 的唯一標識該如何體現。
是用一個 Guid ?還是直接業務標識?
如果不用 Guid ,那么值對象如果需要持久化的話,該怎么辦?
我自己在實踐的時候的標準是,盡量只用業務標識,如果必須要用 Guid,確保它是不可修改,甚至是不可見的。因為 Guid 并不是領域知識的一部分,它只是為了解決技術層面問題而引入的一個東西。
/05 ?Application、DomainService 的區別?/
很多人不重視 DomainService 的設計,其實它非常重要。因為在領域知識中必然存在很多「行為」不屬于任何一個 Entity。比如電商領域中的付款這個動作,到底是放在訂單的 Entity 上?還是賬戶的 Entity 上?其實你會發現都不太適合。此時就是 DomainService 出場的時候了,它避免了某些 Entity 承擔過多與其無關的業務,導致過于臃腫。
Application 的職責類似于一個調度器,用來調度各個業務。但是它不應該體現具體的細節,業務的細節應該被封裝在 Entity、ValueObject 以及 DomainService 中。
有幾個小技巧可以判斷是否體現了業務。
不要有 if / else 分支邏輯
不要有任何針對 Entity、ValueObject 上屬性的讀取和修改操作
概括的來說,Application層只做一件事:將輸入的CQE(Command、Query、Event),通過調用領域對象進行業務處理,然后將結果組裝成 DTO 返回。
/06 ?防腐層在那一層做?Application層?/
我覺得防腐層應該放在 Infrastructure 層,與 IRepository 的實現 Repository 同層,為了便于區分,你也可以將它單獨一個包。
防腐層中的各種 Facade 應該與 Repository 有共同的 IRepository 。因為防腐層背后具體要做的事情就是從其它系統獲取數據,這個職責和 Repository 是一致的。Repository 屏蔽了操作數據庫的細節,而防腐層屏蔽了對接其它系統的細節。
我們這次就聊這么多。以上 6 個問題都是有多個人同時提到過的問題,我想它們也更具普遍性。
最后我們總結一下。
DDD 必須要配合微服務一起用?不是,微服務只是 DDD 思想的一種實現方式。
限界上下文只能通過拆分成獨立的 Service 體現?不是,單體應用中通過「模塊」也可以。
業務簡單的項目不適合 DDD ?不是,DDD 這個方法論對任何項目都有幫助。
Entity 的屬性字段需要對應數據表嗎?無法做到一一對應,因為 Entity 可能存在嵌套。
Application、DomainService 的區別?Application不包含業務,僅對業務進行調度。DomainService 實現不屬于任何一個 Entity 的方法。
防腐層在那一層做?Application 層?Infrastructure 層,與 IRepository 的實現 Repository 同層。
好了,希望對你有所幫助。
最后再講個題外話,「六邊形架構」和「洋蔥架構」在這些年伴隨著 DDD 的概念被普及。其實所謂的六邊形架構,他們的邊就是由 DDD 概念中的防腐層構建的。所謂的洋蔥架構,就是在六邊形架構的基礎上,針對內部業務對象進行建模,而 DDD 就是一種建模思想。
推薦閱讀:
如何擺脫「自我否定」狀態
聊聊Go的三色標記法
原創不易,如果你覺得這篇文章還不錯,就「點贊」或者「在看」一下吧,鼓勵我的創作 :)
也可以分享我的公眾號名片給有需要的朋友們。
如果你有關于軟件架構、分布式系統、產品、運營的困惑
可以試試點擊「閱讀原文」
總結
- 上一篇: Docker小白到实战之常用命令演示,通
- 下一篇: 设计模式之模板方法