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