一篇帶給你DDD領(lǐng)域建模實(shí)戰(zhàn)
大家好,歡迎來到Tlog4J課堂,我是Jensen。
今天給大家分享一篇DDD領(lǐng)域建模實(shí)戰(zhàn),結(jié)合我個(gè)人三年來的DDD實(shí)踐經(jīng)驗(yàn),以企業(yè)級(jí)電商項(xiàng)目DDD領(lǐng)域設(shè)計(jì)為出發(fā)點(diǎn),希望能給到大家對(duì)DDD的一些啟發(fā)。
我會(huì)從DDD領(lǐng)域分析、DDD設(shè)計(jì)呈現(xiàn)、領(lǐng)域建模實(shí)際案例來展開說明,后面會(huì)有彩蛋給到大家~
話不多說,咱們開始DDD之旅吧~
DDD領(lǐng)域分析
講DDD之前,咱們得了解一些基本概念,大家都知道DDD指的是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(Domain-Driven Design),那怎么理解DDD呢?
DDD是一個(gè)事件風(fēng)暴(分類劃分),進(jìn)而知道組織劃分(中臺(tái))、系統(tǒng)劃分(微服務(wù))、代碼劃分/設(shè)計(jì)的思想方法。
這么理解可能比較抽象,其實(shí)它的本質(zhì)就是:通過將復(fù)雜問題簡單化,分而治之,降低復(fù)雜度。
DDD的出現(xiàn),契合了當(dāng)今流行的微服務(wù)架構(gòu),微服務(wù)架構(gòu)有個(gè)特別重要的命題——如何合理劃分微服務(wù),而DDD的戰(zhàn)略設(shè)計(jì)完全可以作為微服務(wù)劃分的依據(jù)。
我們講DDD,它其實(shí)是包括了戰(zhàn)略設(shè)計(jì)和戰(zhàn)術(shù)設(shè)計(jì),那他倆有啥區(qū)別呢?
- 戰(zhàn)略設(shè)計(jì):業(yè)務(wù)層面的領(lǐng)域建模,它強(qiáng)調(diào)業(yè)務(wù)領(lǐng)域模型的識(shí)別與邊界劃分,簡單來說就是業(yè)務(wù)設(shè)計(jì),與代碼呈現(xiàn)無關(guān)。
- 戰(zhàn)術(shù)設(shè)計(jì):工程層面的架構(gòu)設(shè)計(jì)與模型設(shè)計(jì),具體應(yīng)用到微服務(wù)就是完成微服務(wù)設(shè)計(jì)。
這兩者誰更重要呢?我認(rèn)為,戰(zhàn)略設(shè)計(jì)比戰(zhàn)術(shù)設(shè)計(jì)更加重要。
在實(shí)際生產(chǎn)中,業(yè)務(wù)會(huì)越來越復(fù)雜,代碼也會(huì)越壘越龐大,如果業(yè)務(wù)沒有經(jīng)過劃分,表設(shè)計(jì)也沒有經(jīng)過劃分設(shè)計(jì),最后系統(tǒng)將變成一個(gè)龐大的單體應(yīng)用,疊加需求修改代碼的時(shí)候?qū)?huì)牽一發(fā)而動(dòng)全身。
如果業(yè)務(wù)經(jīng)過戰(zhàn)略設(shè)計(jì)合理去劃分,每個(gè)業(yè)務(wù)的業(yè)務(wù)邊界會(huì)顯得特別清晰,即使我們的代碼沒有使用DDD的思想去完成搭建,即使我們還是使用傳統(tǒng)的MVC架構(gòu),也不會(huì)影響業(yè)務(wù)正常運(yùn)作,戰(zhàn)略設(shè)計(jì)是連接產(chǎn)品需求與開發(fā)代碼的一道橋梁。
那領(lǐng)域驅(qū)動(dòng)的戰(zhàn)略設(shè)計(jì)應(yīng)該如何分析呢?我們不需要獨(dú)自去摸索,總結(jié)前輩的一些經(jīng)驗(yàn)即可:
- 通用語言:它的作用是定義上下文含義,以便限界上下文定義領(lǐng)域邊界。
- 事件風(fēng)暴:由項(xiàng)目團(tuán)隊(duì)、領(lǐng)域?qū)<业榷嗳藚⑴c,采用頭腦風(fēng)暴的方式進(jìn)行用戶故事分析,找出并建立領(lǐng)域?qū)ο蟆?/li>
- 四色建模:按時(shí)間發(fā)展先后順序,識(shí)別“追溯單據(jù)”作用的“時(shí)標(biāo)”概念,直達(dá)業(yè)務(wù)核心數(shù)據(jù);它強(qiáng)調(diào)可追溯性與執(zhí)行效率。
- 限界紙筆建模:回到一百年前,在一個(gè)我們沒有計(jì)算機(jī)的年代,我們要做業(yè)務(wù)設(shè)計(jì)會(huì)用什么方法呢?我們可以用紙和筆畫表格并寫實(shí)例,管理核心領(lǐng)域“恰好夠用”的數(shù)據(jù),增強(qiáng)數(shù)據(jù)完整性,避免過度設(shè)計(jì)。
- DCI建模:可能DCI我們聽得比較少,其實(shí)DCI架構(gòu)與MVC架構(gòu)的提出者是同一個(gè)人。DCI建模通過角色扮演模型使得領(lǐng)域模型易于理解,通過小類大對(duì)象的手法避免上帝類的問題;同時(shí)它也能解決貧血模型和充血模型之爭,使模塊更加高內(nèi)聚、低耦合;當(dāng)然,DCI建模也可以與四色建模融合使用。
那么,領(lǐng)域驅(qū)動(dòng)戰(zhàn)術(shù)設(shè)計(jì)應(yīng)該如何落地呢?我認(rèn)為,考慮兩個(gè)大方向即可:
- 模型設(shè)計(jì):比如,使用充血模型還是貧血模型。
- 架構(gòu)設(shè)計(jì):比如,DDD/CQRS、整潔架構(gòu)、六邊形架構(gòu)、清晰架構(gòu)、DCI架構(gòu)……
至此,我們了解清楚了DDD的一些基本概念,那DDD為什么對(duì)我們?nèi)绱酥匾?
我認(rèn)為,DDD可以指導(dǎo)我們業(yè)務(wù)設(shè)計(jì),指導(dǎo)我們代碼落地,使得維護(hù)變得更簡單。
DDD指導(dǎo)設(shè)計(jì):
- 從業(yè)務(wù)領(lǐng)域視角劃分領(lǐng)域邊界,構(gòu)建通用語言進(jìn)行高效溝通,降低團(tuán)隊(duì)新成員對(duì)業(yè)務(wù)的熟悉成本。
- 在不斷交流的過程中,通過提煉領(lǐng)域概念與業(yè)務(wù)抽象建立領(lǐng)域模型,維持業(yè)務(wù)和代碼的邏輯一致性。
- 通過對(duì)領(lǐng)域模型歸類與行為分析,保證實(shí)現(xiàn)業(yè)務(wù)的準(zhǔn)確性。
- 領(lǐng)域建模比數(shù)據(jù)庫建模更輕更全面,而數(shù)據(jù)庫建模不能完全反映系統(tǒng)的全部特性和需求。
DDD指導(dǎo)落地:
- 不過分依賴系統(tǒng)設(shè)計(jì)人員的經(jīng)驗(yàn)背景,指導(dǎo)表設(shè)計(jì)、代碼落地。
- 指導(dǎo)微服務(wù)的設(shè)計(jì)和拆分——?jiǎng)澐殖銮逦奈⒎?wù)邊界、可持續(xù)演進(jìn)的微服務(wù)架構(gòu)。
簡易維護(hù):
- 從討論、設(shè)計(jì),到評(píng)審、落地,以領(lǐng)域模型進(jìn)行交流,可作為系統(tǒng)業(yè)務(wù)的核心載體。
- 以微服務(wù)的維度劃分限界上下文,服務(wù)拆分只需要把相應(yīng)的限界上下文拆出去即可。
- 減少因業(yè)務(wù)需求迭代(如:模型變更)帶來的維護(hù)成本。
這就是DDD對(duì)我們的重要意義,我們不能只以開發(fā)視角去看待業(yè)務(wù)問題,那樣的話就會(huì)陷入開發(fā)思維陷阱,這對(duì)我們的業(yè)務(wù)成長沒有任何幫助。
接下來,咱們來回顧一下,DDD有哪些核心要素——
領(lǐng)域(Domain)
領(lǐng)域是指在特定的范圍或邊界內(nèi)要解決的業(yè)務(wù)問題域,它的核心思想是將業(yè)務(wù)問題域逐級(jí)細(xì)為子域/核心域/通用域/支撐域,降低業(yè)務(wù)理解和系統(tǒng)實(shí)現(xiàn)的復(fù)雜度。
聚合(Aggregate)
聚合內(nèi)包括聚合根、實(shí)體、值對(duì)象,DDD中的聚合是不等同于UML中的聚合的,我們使用充血模型設(shè)計(jì)領(lǐng)域模型的屬性、行為,并識(shí)別聚合與聚合根。
值得注意的是,一個(gè)微服務(wù)最小不要小于一個(gè)聚合,避免引入分布式事務(wù)的復(fù)雜度。
限界上下文(Bounded Context,簡稱BC)
業(yè)務(wù)的通用語言有它的業(yè)務(wù)邊界,我們不大可能用一個(gè)簡單的術(shù)語沒有歧義地去描述一個(gè)復(fù)雜的業(yè)務(wù)領(lǐng)域,BC就是用來細(xì)分領(lǐng)域,從而定義通用語言所在的邊界。
BC包含一個(gè)或多個(gè)聚合,按業(yè)務(wù)領(lǐng)域概念劃分到不同BC,對(duì)應(yīng)Java代碼層面就是頂層包目錄結(jié)構(gòu)。
BC之內(nèi)高內(nèi)聚,一個(gè)業(yè)務(wù)行為在BC內(nèi)盡量使用線程級(jí)別的交互,以保證ACID;BC之間低耦合,可作為微服務(wù)設(shè)計(jì)和拆分的依據(jù),當(dāng)然,微服務(wù)的拆分粒度還需要結(jié)合企業(yè)運(yùn)維的能力。
BC之間最好采用領(lǐng)域事件進(jìn)行交互,或者引入“翻譯器”(或者說防腐層)進(jìn)行通訊,保持BC間的松耦合。
DDD的核心要素就這三個(gè),搞懂這三個(gè)要素,咱們就可以開始搞很多事情了。
當(dāng)然,領(lǐng)域建模的一些難點(diǎn)咱們也要有所了解,比如:
- 領(lǐng)域發(fā)現(xiàn):難點(diǎn)在于領(lǐng)域模型的概念提煉、模型分析與歸類;
- 領(lǐng)域劃分:難點(diǎn)在于業(yè)務(wù)邊界和應(yīng)用邊界如何清晰劃分,如何把控業(yè)務(wù)設(shè)計(jì)的粒度,是自底向上歸納劃分還是自頂向下演繹劃分;
- 領(lǐng)域建模:難點(diǎn)在于如何識(shí)別聚合、聚合根、實(shí)體、值對(duì)象,如何確立領(lǐng)域模型之間的關(guān)系與核心交互等等。
DDD設(shè)計(jì)呈現(xiàn)之——四色建模
在我們接到需求的時(shí)候,會(huì)在腦子里把實(shí)現(xiàn)的代碼過一遍,這對(duì)于簡單的CRUD來說并不難,但是涉及到更復(fù)雜的業(yè)務(wù),一個(gè)場(chǎng)景就夠我們溝通很久才能說清楚,那怎么辦呢?
其實(shí)很簡單,我們需要一個(gè)載體,去把思考的過程沉淀下來,等到產(chǎn)品經(jīng)理來找我們加需求評(píng)估影響范圍的時(shí)候,新人入職給他講解業(yè)務(wù)的時(shí)候,通過這個(gè)載體就能直觀地呈現(xiàn)出來,這就是DDD設(shè)計(jì)呈現(xiàn)的魅力所在。
這里我以我個(gè)人用得比較順手的四色建模法作為DDD的設(shè)計(jì)呈現(xiàn)。
四色建模法是對(duì)領(lǐng)域模型的一種分析方法論,關(guān)注點(diǎn)是領(lǐng)域模型的歸類,它是一種呈現(xiàn)方法。
四色原型延生于90年代,最先由Peter Coad和Mark Mayfield提出[Coad92],然后由David North拓展[Coad95-97],是一種被廣泛使用的系統(tǒng)分析方法。
使用四色建模法設(shè)計(jì)出來的四色圖,它所表達(dá)的類圖是一種包含順序圖的完全動(dòng)態(tài)圖,它是立體多維的,有異于完全靜態(tài)的數(shù)據(jù)庫ER圖。
那四色原型具體是哪四色呢?我們一起來看看:
時(shí)標(biāo)原型(Moment-Interval Archetype,簡稱MI)
表示事物在某個(gè)時(shí)刻或某一段時(shí)間內(nèi)發(fā)生的,如銷售訂單、客戶賬單、收款記錄等,使用淺紅色表示。
PPT原型(Part-Place-Thing Archetype,人/事/物原型,簡稱PPT)
表示參與扮演不同角色的人或事物,如商品、賬戶、店鋪等,使用淺綠色表示。
角色原型(Role Archetype,簡稱ROLE)
抽象了一種參與方式,由人或組織機(jī)構(gòu)、地點(diǎn)或物品來承擔(dān),如客戶、商家、倉儲(chǔ)團(tuán)隊(duì)、財(cái)務(wù)組織等,使用淺黃色表示。
描述原型(Description Archetype,簡稱DESC)
屬于資料類型的資源、目錄式的種類性質(zhì)對(duì)象,或者可以被其他原型反復(fù)使用的,如商品類目、支付方式、方法值對(duì)象等,使用淺藍(lán)色表示。
接下來,咱們使用四色建模法來分析領(lǐng)域模型,總共分為四大步:
- 建立時(shí)標(biāo)原型:尋找需要追溯的事件,根據(jù)追溯事件尋找足跡。
- 建立PPT原型:豐富模型,尋找時(shí)標(biāo)原型周圍的人/事/物,使它可以更好地描述業(yè)務(wù)概念。
- 建立角色原型:進(jìn)一步從中抽象出可以參與到不同流程中去的角色。
- 建立描述原型:把一些信息用描述對(duì)象補(bǔ)足。
這里咱們需要注意的是,整個(gè)過程會(huì)穿插著原型之間關(guān)系/核心交互的標(biāo)注,我們來看一幅電商DDD的四色圖案例:
領(lǐng)域建模實(shí)際案例
如下圖所示,這是財(cái)務(wù)領(lǐng)域模型和支付中心模型的一部分,這里只描述了業(yè)務(wù)系統(tǒng)是如何運(yùn)作起來的,并沒有涉及表的具體字段設(shè)計(jì),全量的模型圖因涉及敏感信息不作詳細(xì)展示:
領(lǐng)域建模就是這么個(gè)建模,這里我提一些設(shè)計(jì)細(xì)節(jié):
- 粉紅色指的是時(shí)標(biāo)原型,是核心業(yè)務(wù)產(chǎn)生的數(shù)據(jù),基本上對(duì)應(yīng)表設(shè)計(jì)。
- 模型屬性不需要體現(xiàn)表的審計(jì)字段,比如通用的ID、創(chuàng)建者、修改時(shí)間、軟刪除標(biāo)識(shí)等,模型行為也只需要設(shè)計(jì)核心行為即可,那種約定俗成的CRUD方法就不需要寫出來了,設(shè)計(jì)要懂得取舍。
- BC內(nèi)模型除了依賴、聚合等等連線,可使用箭頭連接模型之間的核心交互,跨BC之間的模型使用虛線箭頭連接,這里我是結(jié)合了DCI建模法(D表示數(shù)據(jù),C表示上下文、場(chǎng)景,I表示模型間的交互)。
- 對(duì)于表示業(yè)務(wù)唯一的屬性,我使用了加粗展示,再也不用跟別人費(fèi)勁去解析這些模型是用什么維度去建的了
- 對(duì)于還沒上線的屬性變動(dòng)(新增/修改),使用紅色標(biāo)記,因?yàn)轭I(lǐng)域模型圖是指導(dǎo)我們業(yè)務(wù)開發(fā)的。
- 限界上下文的劃分是一種非常主觀的邊界劃分,為了后續(xù)代碼能夠靈活調(diào)整,在Controller的URL設(shè)計(jì)里不需要加上限界上下文。
領(lǐng)域模型圖就像代碼一樣,需要我們長期去維護(hù)的,不是說做完設(shè)計(jì)就不去管了,這一點(diǎn)很重要,微服務(wù)負(fù)責(zé)人一定要有這個(gè)意識(shí)。
關(guān)于DCI建模和DCI架構(gòu)我會(huì)繼續(xù)深入研究,DCI這塊就留到下次再分享叭~
彩蛋!
分享一個(gè)Pro****On在線畫圖平臺(tái)永久白嫖使用的方法:
- 首先買個(gè)一年的Pro****On個(gè)人會(huì)員(159元/年)。
- 建一個(gè)備用的文件夾,里面放各種類型的圖。
- 等到會(huì)員過期后,你還需要建新圖的話,就從備用文件夾里移出來修改使用即可。
我個(gè)人一直都在使用Pro****On在線畫圖平臺(tái),因?yàn)樗梢援嬆X圖和各種UML圖,支持在線協(xié)作,也支持在線圖片嵌入站外(如WIKI)。
當(dāng)然,它還是一個(gè)學(xué)習(xí)設(shè)計(jì)的比較開放的平臺(tái),你可以去它的海量模板庫克隆別人的圖,看看別人是怎么做軟件設(shè)計(jì)的,你也可以把自己畫得賊6的圖付費(fèi)發(fā)布出去,體驗(yàn)一下知識(shí)變現(xiàn)的樂趣,記得要脫敏哦~
好了,這次的DDD領(lǐng)域建模實(shí)戰(zhàn)課就到這里,你學(xué)會(huì)(fei)了嗎?