淺談DDD,你學(xué)會(huì)了嗎?
?DDD 最近幾年越來(lái)越流行,大家都在聊這個(gè)話(huà)題,但是每個(gè)人對(duì)它的理解都不同,小汪哥這里根據(jù)之前在系統(tǒng)拆分、需求評(píng)估,以及遺留系統(tǒng)改造中的一點(diǎn)點(diǎn)經(jīng)驗(yàn),來(lái)淺淺的聊下自己對(duì)DDD的理解。從認(rèn)知定義、作用、領(lǐng)域建模方法、實(shí)現(xiàn)方法論幾個(gè)方面來(lái)聊聊。
認(rèn)知定義
DDD 是一種處理高度復(fù)雜領(lǐng)域的設(shè)計(jì)思想,它試圖分離技術(shù)實(shí)現(xiàn)的復(fù)雜性,并圍繞業(yè)務(wù)概念構(gòu)建領(lǐng)域模型來(lái)控制業(yè)務(wù)的復(fù)雜性,以解決軟件難以理解,難以演進(jìn)的問(wèn)題。
DDD 不是架構(gòu),而是一種架構(gòu)設(shè)計(jì)方法論,它通過(guò)邊界劃分將復(fù)雜業(yè)務(wù)領(lǐng)域簡(jiǎn)單化,幫我們?cè)O(shè)計(jì)出清晰的領(lǐng)域和應(yīng)用邊界,可以很容易地實(shí)現(xiàn)架構(gòu)演進(jìn)和微服務(wù)的落地。
作用
優(yōu)勢(shì):
1、作為微服務(wù)的定義的指導(dǎo)思想。
2、理解業(yè)務(wù)的一種方法論,可以在接手遺留系統(tǒng),以及遺留系統(tǒng)改造時(shí)快速理解業(yè)務(wù)。
3、解決領(lǐng)域知識(shí)被割裂肢解、代碼的業(yè)務(wù)語(yǔ)義表達(dá)能力弱的問(wèn)題。
4、控制系統(tǒng)復(fù)雜度,控制代碼量。
劣勢(shì):
1、DDD不能解決大部分的性能優(yōu)化問(wèn)題,甚至大部分的場(chǎng)景,我們需要為性能優(yōu)化去做反DDD設(shè)計(jì)。
2、DDD不能解決開(kāi)發(fā)技術(shù)水平的問(wèn)題。
3、DDD需要我們?cè)陬I(lǐng)域建模花費(fèi)很多的時(shí)間和精力,而且還可能導(dǎo)致付出和收益不成正比的情況。
領(lǐng)域建模方法
領(lǐng)域建模解決的問(wèn)題
領(lǐng)域建模的目的是統(tǒng)一大家的業(yè)務(wù)認(rèn)知,讓業(yè)務(wù)、開(kāi)發(fā)、測(cè)試、產(chǎn)品在同一個(gè)頻道上交流。其實(shí)要做到這一點(diǎn)是很難的,開(kāi)發(fā)喜歡從技術(shù)層面去描述問(wèn)題,產(chǎn)品習(xí)慣從業(yè)務(wù)層面描述問(wèn)題,兩個(gè)不在同一個(gè)頻道怎么能好好溝通。在小團(tuán)隊(duì)這種優(yōu)勢(shì)表現(xiàn)不出來(lái),在大團(tuán)隊(duì)中,溝通成本是很高的。
領(lǐng)域建模,說(shuō)的很簡(jiǎn)單,但是做好確實(shí)很難,一個(gè)復(fù)雜的需求不是建幾個(gè)實(shí)體對(duì)象就能解決的。從全局看只在腦海中進(jìn)行的建模實(shí)際上并不一定正確和穩(wěn)定。因此我們需要找到正確的方法幫助對(duì)業(yè)務(wù)領(lǐng)域進(jìn)行分析,得到建模結(jié)構(gòu),共享建模成果。值得慶幸的是,前輩及牛人已經(jīng)總結(jié)了一些建模方法。
常用的建模方法有:用例分析法、四色建模法、事件風(fēng)暴法。這個(gè)我就不一一贅述了,網(wǎng)上有很多內(nèi)容,或者公眾號(hào)回復(fù)【DDD】獲取相關(guān)資料。
實(shí)現(xiàn)方法論
戰(zhàn)略設(shè)計(jì):
戰(zhàn)略設(shè)計(jì)主要從業(yè)務(wù)視角出發(fā),建立業(yè)務(wù)領(lǐng)域模型,劃分領(lǐng)域邊界,建立通用語(yǔ)言的限界上下文,限界上下文可以作為微服務(wù)設(shè)計(jì)的參考邊界。
各種域:
核心域、支撐域和通用域的主要目標(biāo)是:通過(guò)領(lǐng)域劃分,區(qū)分不同子域在公司內(nèi)的不同功能屬性和重要性,從而公司可對(duì)不同子域采取不同的資源投入和建設(shè)策略,其關(guān)注度也會(huì)不一樣。
統(tǒng)一語(yǔ)言:
統(tǒng)一語(yǔ)言提供了一種更好的協(xié)同方式的可能性。統(tǒng)一語(yǔ)言與其背后的領(lǐng)域模型賦予了研發(fā)人員通過(guò)重構(gòu)定義業(yè)務(wù)的能力,在業(yè)務(wù)方大多強(qiáng)勢(shì)的環(huán)境中,難能可貴地建立了技術(shù)反饋業(yè)務(wù)的途徑,降低了知識(shí)消化過(guò)程失敗的風(fēng)險(xiǎn)。
圖片來(lái)源:《如何落地業(yè)務(wù)建?!?/p>
限界上下文:
限界上下文是微服務(wù)設(shè)計(jì)和拆分的主要依據(jù)。在領(lǐng)域模型中,如果不考慮技術(shù)異構(gòu)、團(tuán)隊(duì)溝通等其它外部因素,一個(gè)限界上下文理論上就可以設(shè)計(jì)為一個(gè)微服務(wù)。
限界上下文的定義就是:用來(lái)封裝通用語(yǔ)言和領(lǐng)域?qū)ο?,提供上下文環(huán)境,保證在領(lǐng)域之內(nèi)的一些術(shù)語(yǔ)、業(yè)務(wù)相關(guān)對(duì)象等(通用語(yǔ)言)有一個(gè)確切的含義,沒(méi)有二義性。
正如電商領(lǐng)域的商品一樣,商品在不同的階段有不同的術(shù)語(yǔ),在銷(xiāo)售階段是商品,而在運(yùn)輸階段則變成了貨物。同樣的一個(gè)東西,由于業(yè)務(wù)領(lǐng)域的不同,賦予了這些術(shù)語(yǔ)不同的涵義和職責(zé)邊界,這個(gè)邊界就可能會(huì)成為未來(lái)微服務(wù)設(shè)計(jì)的邊界??吹竭@,領(lǐng)域邊界就是通過(guò)限界上下文來(lái)定義的。
戰(zhàn)術(shù)設(shè)計(jì):
戰(zhàn)術(shù)設(shè)計(jì)則從技術(shù)視角出發(fā),側(cè)重于領(lǐng)域模型的技術(shù)實(shí)現(xiàn),完成軟件開(kāi)發(fā)和架構(gòu)落地,包括:聚合根、實(shí)體、值對(duì)象等代碼邏輯及代碼分層的設(shè)計(jì)和實(shí)現(xiàn)。主要討論在一個(gè)服務(wù)內(nèi)部,如何劃分和組織代碼。
實(shí)體和值對(duì)象:
實(shí)體和值對(duì)象:從領(lǐng)域模型的基礎(chǔ)單元看系統(tǒng)設(shè)計(jì)實(shí)體和值對(duì)象是組成領(lǐng)域模型的基礎(chǔ)單元。
實(shí)體的代碼形態(tài)
在代碼模型中,實(shí)體的表現(xiàn)形式是實(shí)體類(lèi),這個(gè)類(lèi)包含了實(shí)體的屬性和方法,通過(guò)這些方法實(shí)現(xiàn)實(shí)體自身的業(yè)務(wù)邏輯。在 DDD 里,這些實(shí)體類(lèi)通常采用充血模型,與這個(gè)實(shí)體相關(guān)的所有業(yè)務(wù)邏輯都在實(shí)體類(lèi)的方法中實(shí)現(xiàn),跨多個(gè)實(shí)體的領(lǐng)域邏輯則在領(lǐng)域服務(wù)中實(shí)現(xiàn)。
實(shí)體以 DO(領(lǐng)域?qū)ο螅┑男问酱嬖?,每個(gè)實(shí)體對(duì)象都有唯一的 ID。我們可以對(duì)一個(gè)實(shí)體對(duì)象進(jìn)行多次修改,修改后的數(shù)據(jù)和原來(lái)的數(shù)據(jù)可能會(huì)大不相同。但是,由于它們擁有相同的 ID,它們依然是同一個(gè)實(shí)體。
實(shí)體的數(shù)據(jù)庫(kù)形態(tài)?
在領(lǐng)域模型映射到數(shù)據(jù)模型時(shí),一個(gè)實(shí)體可能對(duì)應(yīng) 0 個(gè)、1 個(gè)或者多個(gè)數(shù)據(jù)庫(kù)持久化對(duì)象。大多數(shù)情況下實(shí)體與持久化對(duì)象是一對(duì)一。在某些場(chǎng)景中,有些實(shí)體只是暫駐靜態(tài)內(nèi)存的一個(gè)運(yùn)行態(tài)實(shí)體,它不需要持久化。
值對(duì)象?
值對(duì)象相對(duì)實(shí)體來(lái)說(shuō),會(huì)更加抽象一些。簡(jiǎn)單來(lái)說(shuō),值對(duì)象本質(zhì)上就是一個(gè)集合。
值對(duì)象的代碼形態(tài)?
值對(duì)象在代碼中有這樣兩種形態(tài)。如果值對(duì)象是單一屬性,則直接定義為實(shí)體類(lèi)的屬性;如果值對(duì)象是屬性集合,則把它設(shè)計(jì)為 Class 類(lèi),Class 將具有整體概念的多個(gè)屬性歸集到屬性集合,這樣的值對(duì)象沒(méi)有 ID,會(huì)被實(shí)體整體引用。
圖片來(lái)源:《DDD 實(shí)戰(zhàn)課》
例如上圖:
人員實(shí)體原本包括:姓名、年齡、性別以及人員所在的省、市、縣和街道等屬性。這樣顯示地址相關(guān)的屬性就很零碎了對(duì)不對(duì)?現(xiàn)在,我們可以將“省、市、縣和街道等屬性”拿出來(lái)構(gòu)成一個(gè)“地址屬性集合”,這個(gè)集合就是值對(duì)象了。
聚合和聚合根:
領(lǐng)域模型內(nèi)的實(shí)體和值對(duì)象就好比個(gè)體,而能讓實(shí)體和值對(duì)象協(xié)同工作的組織就是聚合,它用來(lái)確保這些領(lǐng)域?qū)ο笤趯?shí)現(xiàn)共同的業(yè)務(wù)邏輯時(shí),能保證數(shù)據(jù)的一致性。聚合就是由業(yè)務(wù)和邏輯緊密關(guān)聯(lián)的實(shí)體和值對(duì)象組合而成的,聚合是數(shù)據(jù)修改和持久化的基本單元,每一個(gè)聚合對(duì)應(yīng)一個(gè)倉(cāng)儲(chǔ),實(shí)現(xiàn)數(shù)據(jù)的持久化。聚合有一個(gè)聚合根和上下文邊界(一個(gè)聚合包含了多個(gè)實(shí)體對(duì)象和值對(duì)象,其中有一個(gè)實(shí)體對(duì)象做為聚合根。這些對(duì)象聚集在一起形成了一個(gè)比較完整獨(dú)立的業(yè)務(wù)邊界,稱(chēng)為上下文邊界。),這個(gè)邊界根據(jù)業(yè)務(wù)單一職責(zé)和高內(nèi)聚原則,定義了聚合內(nèi)部應(yīng)該包含哪些實(shí)體和值對(duì)象,而聚合之間的邊界是松耦合的。按照這種方式設(shè)計(jì)出來(lái)的微服務(wù)很自然就是“高內(nèi)聚、低耦合”的。
我們以保險(xiǎn)的投保業(yè)務(wù)場(chǎng)景為例,看一下聚合的構(gòu)建過(guò)程主要都包括哪些步驟:
圖片來(lái)源:《DDD 實(shí)戰(zhàn)課》
聚合根?
聚合根 leave 中有屬性、值對(duì)象、關(guān)聯(lián)實(shí)體和自身的業(yè)務(wù)行為。Leave 實(shí)體采用充血模型 ,有自己的業(yè)務(wù)行為,具體就是聚合根實(shí)體類(lèi)的方法,如代碼中的 getDuration 和 addHistoryApprovalInfo 等方法。
聚合根引用實(shí)體和值對(duì)象,它可以組合聚合內(nèi)的多個(gè)實(shí)體,在聚合根實(shí)體類(lèi)方法中完成復(fù)雜的業(yè)務(wù)行為,這種復(fù)雜的業(yè)務(wù)行為也可以在聚合領(lǐng)域服務(wù)里實(shí)現(xiàn)。但為了職責(zé)和邊界清晰,我建議聚合要根據(jù)自身的業(yè)務(wù)行為在實(shí)體類(lèi)方法中實(shí)現(xiàn),而涉及多個(gè)實(shí)體組合才能實(shí)現(xiàn)的業(yè)務(wù)能力由領(lǐng)域服務(wù)完成。下面是聚合根 leave 的實(shí)體類(lèi)方法,它包含屬性、對(duì)實(shí)體和值對(duì)象的引用以及自己的業(yè)務(wù)行為和方法。
DDD分層架構(gòu)
最后就是如何組織代碼的問(wèn)題,這個(gè)時(shí)候就需要要到DDD的分層架構(gòu)。
那么從之前的MVC三層架構(gòu)如何演變成DDD的分層架構(gòu)呢?
DDD分層架構(gòu)與MVC架構(gòu)的映射關(guān)系:
在《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)——軟件核心復(fù)雜性應(yīng)對(duì)之道》書(shū)中也描述了各層的關(guān)系:
不過(guò)小汪哥覺(jué)得,代碼的組織方式可以根據(jù)團(tuán)隊(duì)的情況來(lái)調(diào)整,只要能符合領(lǐng)域驅(qū)動(dòng)的思想即可。
各個(gè)層級(jí)的作用可以參考之前的文章:領(lǐng)域驅(qū)動(dòng)落地實(shí)戰(zhàn)?,這里就不在一一贅述了。
小結(jié)
本文主要從DDD是什么,能干什么,不能干什么,怎么干(領(lǐng)域建模方法、實(shí)現(xiàn)方法論)幾個(gè)方面來(lái)聊了一下領(lǐng)域驅(qū)動(dòng),當(dāng)然,一千個(gè)人有一千種對(duì)領(lǐng)域驅(qū)動(dòng)的理解。?