輯導(dǎo)語:DDD(Domain-driven design,領(lǐng)域驅(qū)動設(shè)計)是一種架構(gòu)設(shè)計方法論,通過邊界劃分,將復(fù)雜業(yè)務(wù)領(lǐng)域簡單化,幫助我們設(shè)計出清晰的領(lǐng)域和應(yīng)用邊界,保證業(yè)務(wù)模型與代碼模型的一致性。本文作者結(jié)合實際經(jīng)驗,介紹了如何從0到1實踐DDD,一起來看看吧。
隨著業(yè)務(wù)的不斷發(fā)展,我們發(fā)現(xiàn)自己的系統(tǒng)開始變得有點(diǎn)臃腫,為了減少復(fù)雜性,我們嘗試借助DDD來改善我們的系統(tǒng)。本文記錄了自己對DDD的理解和實踐過程,歡迎大家一起探討。見識所限,難免有理解不到位,希望路過的大佬不吝賜教。
如果你有以上的一些疑問,那你可以試試領(lǐng)域驅(qū)動設(shè)計:
DDD(Domain-driven design,領(lǐng)域驅(qū)動設(shè)計)是一種架構(gòu)設(shè)計方法論,通過邊界劃分,將復(fù)雜業(yè)務(wù)領(lǐng)域簡單化,幫助我們設(shè)計出清晰的領(lǐng)域和應(yīng)用邊界,保證業(yè)務(wù)模型與代碼模型的一致性。
在細(xì)看這個定義之前,我們可以思考一下,為什么我們的業(yè)務(wù)系統(tǒng)會慢慢變得復(fù)雜?
常見的情況是,業(yè)務(wù)在發(fā)展過程中為了探尋發(fā)力點(diǎn),需要不斷地試錯迭代,調(diào)整方向,而系統(tǒng)在設(shè)計之初,難以預(yù)期到后面的瞬息萬變,為了應(yīng)付業(yè)務(wù),修修改改,久之,系統(tǒng)也變得復(fù)雜起來。
可以怎么辦呢?及時重構(gòu)唄——不改變軟件系統(tǒng)外部行為的前提下,改善它的內(nèi)部結(jié)構(gòu)。
然而重構(gòu)是從技術(shù)層面上抽煉出來的模型,往往不具有實際的業(yè)務(wù)含義,其他同學(xué)可能難以自然地將業(yè)務(wù)問題映射到對應(yīng)的設(shè)計模型。另外,如果不能如實映射業(yè)務(wù)模型,隨著業(yè)務(wù)方向調(diào)整,代碼可能又開始腐敗……有點(diǎn)像芝諾悖論中,阿基里斯永遠(yuǎn)追不上小烏龜。
那DDD怎么搞?
DDD是這么想的:”將業(yè)務(wù)架構(gòu)映射到系統(tǒng)架構(gòu)上,在響應(yīng)業(yè)務(wù)變化調(diào)整業(yè)務(wù)架構(gòu)時,也隨之變化系統(tǒng)架構(gòu)”。可能大家平時有這樣的想法,但是比較模糊,未形成體系,而DDD就提供了一套完整的方法論。從業(yè)務(wù)角度去審視我們的系統(tǒng),從而實現(xiàn)高內(nèi)聚低耦合的代碼。
整體而言,領(lǐng)域驅(qū)動設(shè)計包括戰(zhàn)略建模和戰(zhàn)術(shù)建模: 戰(zhàn)略設(shè)計側(cè)重于高層次、宏觀上去劃分和集成限界上下文,而戰(zhàn)術(shù)設(shè)計則關(guān)注更具體使用建模工具來細(xì)化上下文。
1)領(lǐng)域、子域
在討論問題之前,我們需要先定義好問題。
領(lǐng)域即問題域,通常是根據(jù)一個組織所處的行業(yè)進(jìn)行識別,它基于業(yè)務(wù)的愿景,定義了系統(tǒng)要解決的現(xiàn)實問題的目標(biāo)和范圍。領(lǐng)域越大,業(yè)務(wù)的范圍也越大,大的領(lǐng)域可以拆分成小的問題域,稱之為子域。根據(jù)子域重要性和功能屬性劃,可以將其分為三類。
核心域、支撐域和通用域:
這個幾個概念其實很容易理解,不過在劃分的時候,注意要從業(yè)務(wù)的視角,而不是技術(shù)功能模塊來劃分。
2)限界上下文
我們語言博大精深,同樣的話在不同語境下就可演變出不同含義,這在溝通時總是帶來不必要的麻煩。為了準(zhǔn)確地溝通,我們需要統(tǒng)一語言的邊界,在相同的語言邊界內(nèi)溝通,才不容易出差錯。
一則阿凡提當(dāng)理發(fā)師懲罰一個狡猾牧師的趣事:理發(fā)時,阿凡提刮臉時問牧師:“牧師,是否要眉毛?”牧師答:“這還用問,眉毛豈能不要?”.“好,你要我就給你!”,說著就把牧師的眉毛刮下來遞到他手里,牧師氣得說不出話來,誰叫自己說要呢。阿凡提又問:“牧師,胡子要嗎?”.“不要,不要!”牧師連忙說。“好,你不要就不要。” 嗖嗖幾刀就把牧師的胡子刮下來。
在一個系統(tǒng)中,一個名詞在不同語境可能有不同的含義,我們對它關(guān)注的屬性和行為也有所不同。例如,在電商系統(tǒng)中,對于產(chǎn)品Product, 在采購上下文,需要關(guān)注產(chǎn)品的進(jìn)價、最小起訂量與供貨周期;在市場上下文中,則關(guān)心產(chǎn)品的品質(zhì)、售價,以及用于促銷的精美圖片和銷售類型;在倉儲上下文中,倉庫工作人員更關(guān)心產(chǎn)品放在倉庫的哪個位置,產(chǎn)品的重量與體積,是否易碎品以及訂購產(chǎn)品的數(shù)量。
限界上下文在《實現(xiàn)領(lǐng)域驅(qū)動設(shè)計》中,用了很大篇幅去講,它有幾個重要的意義:
經(jīng)過戰(zhàn)略建模之后,我們可以得到以下的一個模型:
為了更好地理解,我們對手上的一個項目:“IoT設(shè)備增值產(chǎn)品管理系統(tǒng)”進(jìn)行實踐。該項目中,我們提供給商戶在IoT設(shè)備上管理增值運(yùn)營產(chǎn)品的能力。這里的IoT設(shè)備主要是微信支付刷臉設(shè)備等。商戶可以在系統(tǒng)中創(chuàng)建我們業(yè)務(wù)中的增值運(yùn)營產(chǎn)品,如電子海報、互動海報等,創(chuàng)建完之后,相關(guān)的增值產(chǎn)品會被投放到IoT設(shè)備上,進(jìn)行展示、運(yùn)作:
一開始我們從業(yè)務(wù)的用例出發(fā),認(rèn)為我們的系統(tǒng)主要是商戶在我們頁面網(wǎng)站使用,以及IoT設(shè)備通過接口連接我們后臺服務(wù),認(rèn)為這兩個分屬不同的子域,然后梳理了一些支撐的功能:
畫完草圖之后,感覺不是很確定,于是便去咨詢部門的DDD專家王老師(十分感謝王立老師的指導(dǎo)),得到了一些寶貴的建議:我們應(yīng)該避免直接從表現(xiàn)層去看業(yè)務(wù),表現(xiàn)層就像是冰山露在水面上的棱角,這些棱角看起來毫不相干,但是實際上底層是連成一塊的,這些才是我們需要關(guān)注的。
就像這個項目,表面上商戶和設(shè)備是分開的,實際上它們在操作都是我們的增值運(yùn)營產(chǎn)品,應(yīng)該看成我們的系統(tǒng)提供統(tǒng)一對外的服務(wù),然后商戶和設(shè)備來使用我們的服務(wù)。UGC內(nèi)容存儲業(yè)務(wù)用例其實沒有涉及到的,屬于實現(xiàn)時候的東西。一番建議讓我們理清了思路,于是重新梳理,得到以下的戰(zhàn)略建模圖:
整體而言,我們將整體系統(tǒng)梳理為8個子域:
其中我們系統(tǒng)中的商戶信息依賴了微信支付商戶賬號信息和IoT設(shè)備鋪設(shè)服務(wù)信息,這里使用防腐層進(jìn)行隔離,將外部的商戶信息“翻譯”為我們業(yè)務(wù)中的商戶信息。三、如何實現(xiàn)DDD之戰(zhàn)術(shù)建模梳理清楚上下文之間的關(guān)系后,我們基本了解業(yè)務(wù)的概貌,接下來需要細(xì)化上下文,進(jìn)一步完善我們的模型。這里也需要用到DDD的一些基本概念。
1)實體、值對象
實體和值對象是組成領(lǐng)域模型的基礎(chǔ)單元。當(dāng)一個對象由其標(biāo)識(而不是屬性)區(qū)分時,這種對象稱為實體。如在校園教務(wù)系統(tǒng)中,每個賬戶是對應(yīng)著一個學(xué)生,根據(jù)學(xué)號來唯一標(biāo)識,可以認(rèn)為是一個實體。傳統(tǒng)的數(shù)據(jù)建模大多是根據(jù)數(shù)據(jù)庫范式設(shè)計的,每一個數(shù)據(jù)庫表對應(yīng)一個實體,每一個實體的屬性值用單獨(dú)的一列來存儲,一個實體主表會對應(yīng) N 個實體從表。
與其不同,DDD 是先構(gòu)建領(lǐng)域模型,再將業(yè)務(wù)對象映射為持久化對象。這可能導(dǎo)致DDD建立出來的實體,映射到具體數(shù)據(jù)庫表時,可能是1對多,多對1的關(guān)系。
如一個賬戶實體,有它的基本信息和權(quán)限角色信息,可能就對應(yīng)了2個持久化對象。另一方面,有時候為了某些查詢場景的方便,會把教師賬戶、學(xué)生賬戶等對應(yīng)成一個持久化對象,就成了多對1。
通過對象屬性值來識別的對象,則可以認(rèn)為是一個值對象。如地址信息{“省”: “廣東省”,”市”:”深圳市”},我們是通過它的屬性來區(qū)分出不同的地址。值對象實際上是想把一些不變的屬性組合起來,減少系統(tǒng)的復(fù)雜性。在設(shè)計值對象的時候,需要滿足以下的特性:
2)聚合、聚合根
在 DDD 中,實體和值對象是基礎(chǔ)的領(lǐng)域?qū)ο蟆嶓w一般對應(yīng)業(yè)務(wù)對象,它具有業(yè)務(wù)屬性和業(yè)務(wù)行為;而值對象主要是屬性集合,對實體的狀態(tài)和特征進(jìn)行描述。但是我們的一個業(yè)務(wù)流程中,一般會同時涉及多個實體、值對象的操作,這里業(yè)務(wù)邏輯緊密的實體和值對象便組合成一個聚合。
從數(shù)據(jù)層面來看,同個聚合內(nèi)的數(shù)據(jù)需要保持強(qiáng)一致性。
每一個聚合有一個聚合根實體,設(shè)置聚合根的主要目的是為了避免由于復(fù)雜數(shù)據(jù)模型缺少統(tǒng)一的業(yè)務(wù)規(guī)則控制,而導(dǎo)致聚合、實體之間數(shù)據(jù)不一致性的問題。聚合根可以看成是聚合的管理者,或是說handle。對內(nèi)其協(xié)調(diào)實體和值對象完成業(yè)務(wù)邏輯。對外則提供通過聚合ID供其他聚合關(guān)聯(lián)引用,屏蔽外部對內(nèi)部實體的直接訪問和修改。
建議的聚合設(shè)計原則:
3)領(lǐng)域服務(wù)
領(lǐng)域服務(wù)的定義:領(lǐng)域中的服務(wù)表示一個無狀態(tài)的操作,它用于實現(xiàn)特定于某個領(lǐng)域的任務(wù)。當(dāng)某個操作不適合放在聚合(實體)或值對像上時,最好的方式便是使用領(lǐng)域服務(wù)。
舉個例子,在一個路線導(dǎo)航的項目中,“路線”可能是其中的一個實體,如果業(yè)務(wù)中有“推薦路線上相關(guān)的美食”這樣一個功能,那我們會想,這個功能應(yīng)該歸給哪個領(lǐng)域?qū)ο螅o“路線”實體嗎?有點(diǎn)不合適,應(yīng)該路線本身關(guān)注的是起終點(diǎn),時間人物等。
此時可以將其這個功能歸為領(lǐng)域服務(wù),它是一個路線狀態(tài)無關(guān)的服務(wù),輸入路線各個節(jié)點(diǎn),來得到沿路的各種美食。當(dāng)然,要注意不要過度地使用領(lǐng)域服務(wù),因為這很可能導(dǎo)致你把實體的行為都放在里面了,實體本身都變成了一些只有g(shù)etter和setter的“貧血模型”。
4)領(lǐng)域事件
領(lǐng)域事件是領(lǐng)域模型中非常重要的一部分,用來表示領(lǐng)域中發(fā)生的事件。一個領(lǐng)域事件將導(dǎo)致進(jìn)一步的業(yè)務(wù)操作,在實現(xiàn)業(yè)務(wù)解耦的同時,還有助于形成完整的業(yè)務(wù)閉環(huán)。
領(lǐng)域事件含義很廣泛,可以是業(yè)務(wù)流程的一個步驟,也可以是一個事件發(fā)生后觸發(fā)的后續(xù)動作,繳費(fèi)完成之后,觸發(fā)短信通知;上面在設(shè)計聚合的時候,我們提到一個原則:在邊界之外使用最終一致性,一次事務(wù)最多只能更改一個聚合的狀態(tài)。如果一次業(yè)務(wù)操作涉及多個聚合狀態(tài)的更改,應(yīng)通過領(lǐng)域事件,達(dá)到最終一致性。
實際上是通過事件驅(qū)動的這種異步方式,對系統(tǒng)進(jìn)行解耦。當(dāng)然,如果你覺得某兩個步驟,業(yè)務(wù)流程上不允許是不一致的,那就得重新考慮將其歸在同個聚合中了。
我們以增值運(yùn)營服務(wù)上下文為例,根據(jù)上面的理解,結(jié)合業(yè)務(wù)實際,得到以下模型:
其中增值產(chǎn)品是其中的一個聚合根,通過該聚合根進(jìn)行各種領(lǐng)域操作。海報縮略圖是其中的一個領(lǐng)域服務(wù),通過輸入產(chǎn)品素材中的海報url,來得到一個海報的縮略圖。
在《領(lǐng)域驅(qū)動設(shè)計——軟件核心復(fù)雜性的應(yīng)對之道》一書中,Eric提出了這樣的一種分層結(jié)構(gòu),將整個系統(tǒng)劃分為四層:用戶接口層、應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)設(shè)施層。
用戶接口層:用戶接口層負(fù)責(zé)向用戶顯示信息和解釋用戶指令。
應(yīng)用層:應(yīng)用層相對來說是較“薄”的一層,主要是部署了應(yīng)用服務(wù)。應(yīng)用服務(wù)的實現(xiàn)中,它負(fù)責(zé)編排和轉(zhuǎn)發(fā)下一層的領(lǐng)域?qū)拥慕涌冢瑢⒁獙崿F(xiàn)的功能委托給一個或多個領(lǐng)域?qū)ο髞韺崿F(xiàn),本身只負(fù)責(zé)處理業(yè)務(wù)用例的執(zhí)行順序以及結(jié)果的拼裝。
領(lǐng)域?qū)樱?/strong>領(lǐng)域?qū)邮潜容^“厚”的一層,它包含聚合根、實體、值對象、領(lǐng)域服務(wù)等領(lǐng)域模型中的領(lǐng)域?qū)ο螅瑢崿F(xiàn)了核心的業(yè)務(wù)邏輯。領(lǐng)域?qū)雍蛻?yīng)用層的職責(zé)看起來有點(diǎn)模糊。
個人覺得,可以理解是應(yīng)用層描述了一個具體操作從開始到結(jié)束的每一個環(huán)節(jié),而領(lǐng)域?qū)觿t是對其的細(xì)化,用來處理具體的某一個環(huán)節(jié)。
比如,比如線上購物中,購物車結(jié)算這一場景可看成是一個應(yīng)用行為。而這個行為又主要包括金額計算、支付、生成訂單,這些子環(huán)節(jié)就可以理解為一個領(lǐng)域?qū)拥姆?wù)了。
基礎(chǔ)設(shè)施層:可以看到上面三層都有箭頭指向基礎(chǔ)設(shè)施層,它的作用就是為其它各層提供通用的技術(shù)和基礎(chǔ)服務(wù),如數(shù)據(jù)持久化、消息中間件等DDD 分層架構(gòu)中的要素與傳統(tǒng)三層架構(gòu)(用戶界面層、業(yè)務(wù)邏輯層、數(shù)據(jù)訪問層)還是挺相似的,一個主要的變化是將業(yè)務(wù)邏輯層的服務(wù)拆分到了應(yīng)用層和領(lǐng)域?qū)印?yīng)用層響應(yīng)業(yè)務(wù)用例的變化,領(lǐng)域?qū)雨P(guān)注不變的領(lǐng)域模型。
圖片來自極客時間
《DDD實戰(zhàn)課》在實際的代碼工程便是按照這樣的目錄來劃分,最近部門在推的整潔Git,也是這樣劃分目錄:
接下來,便是將領(lǐng)域?qū)ο笥成涞綄嶋H的類,實現(xiàn)對應(yīng)的屬性和行為。當(dāng)然,具體實現(xiàn)中有很多范式可參考和討論,我們也在摸索中,待后續(xù)慢慢補(bǔ)充……
DDD首先不是關(guān)于技術(shù)的,而是關(guān)于討論、聆聽、理解、發(fā)現(xiàn)業(yè)務(wù)價值的。——Vaughn Vernon《實現(xiàn)領(lǐng)域驅(qū)動設(shè)計》
如Vernon所說的,DDD首先是關(guān)注業(yè)務(wù)的價值的。一開始我們對業(yè)務(wù)的邊界、目標(biāo)可能有個大概了解,但是見解還是不盡相同。
通過一起對業(yè)務(wù)的討論與思考,我們了解了業(yè)務(wù)的概貌及核心,明確價值所在。關(guān)注到了核心,自然可以幫助我們實現(xiàn)與業(yè)務(wù)契合的系統(tǒng)。通過這次學(xué)習(xí)與實踐,我們進(jìn)一步接觸了DDD。
當(dāng)然,這也還只是開始,更多的關(guān)聯(lián)知識還隱藏在冰山之下。同時我們也明白,DDD也只是一種方法論上的參考,不是“銀彈”,需要不斷地去實踐與思考,才能體會出它的價值。
參考:
作者:bryanzhao,微信支付后臺開發(fā)工程師
本文由 @騰訊大講堂 原創(chuàng)發(fā)布于人人都是產(chǎn)品經(jīng)理,未經(jīng)許可,禁止轉(zhuǎn)載。
題圖來自 Pixabay,基于CC0協(xié)議。
在說什么是領(lǐng)域驅(qū)動設(shè)計之前,我覺得需要先說一下我們?yōu)槭裁葱枰I(lǐng)域驅(qū)動,我個人認(rèn)為領(lǐng)域驅(qū)動設(shè)計對于研發(fā)來說改進(jìn)點(diǎn)主要有下面三個:
領(lǐng)域驅(qū)動設(shè)計(DDD)是一種軟件設(shè)計思路,領(lǐng)域指的是業(yè)務(wù)領(lǐng)域,比如銀行業(yè)務(wù)領(lǐng)域,醫(yī)藥銷售領(lǐng)域;不同于傳統(tǒng)以數(shù)據(jù)表為中心的建模方式,它以業(yè)務(wù)領(lǐng)域為中心來建模,能促使我們以正確的方式使用面向?qū)ο螅枬M的領(lǐng)域?qū)ο蟆?/p>
在進(jìn)行領(lǐng)域建模和開發(fā)時我們主要需要關(guān)注以下幾點(diǎn):
下面的思維導(dǎo)圖是領(lǐng)域驅(qū)動設(shè)計需要掌握的一些點(diǎn):
Tips:
1.領(lǐng)域驅(qū)動設(shè)計是2004年Eric Evans在《領(lǐng)域驅(qū)動設(shè)計:軟件核心復(fù)雜性應(yīng)對之道》這本書中提到的,DDD的出現(xiàn)是為了應(yīng)對復(fù)雜業(yè)務(wù)場景下軟件越來越復(fù)雜問題;也就是說通過DDD可以將軟件的復(fù)雜度控制在合理的范圍內(nèi)。
2.領(lǐng)域驅(qū)動設(shè)計主要解決的是業(yè)務(wù)復(fù)雜度問題(避免大泥球風(fēng)格:大泥球風(fēng)格就是沒有任何清楚的結(jié)構(gòu),例如隨意共享的數(shù)據(jù),隨意全局化的數(shù)據(jù)結(jié)構(gòu)。這樣風(fēng)格的系統(tǒng)可維護(hù)性(maintainability)和可擴(kuò)展性(extensibility)都很差,最終導(dǎo)致整個系統(tǒng)難以改動,維護(hù)不下去),如果業(yè)務(wù)不復(fù)雜,則不需要使用DDD方式來處理(推薦用三層架構(gòu))。
領(lǐng)域驅(qū)動設(shè)計的優(yōu)缺點(diǎn)很明顯,我這邊整理了幾個優(yōu)缺點(diǎn),供參考。
優(yōu)點(diǎn):
缺點(diǎn):
按照實現(xiàn)領(lǐng)域驅(qū)動設(shè)計一書中描述的DDD步驟主要有4步:
軟件開發(fā)實際作用就是為客戶解決問題或者軟件升級變革,所以在領(lǐng)域中我們劃分了問題空間和解空間兩個緯度空間,用于描述客戶的問題(需求)和如何解決問題(解決方案)。
問題的來源:
首先我們需要對問題域進(jìn)行拆分,拆分的理念就是由大化小,逐個擊破。拆分完畢問題空間,我們需要做到逐一擊破,為每一個問題形成一套落地解決方案。一個問題對應(yīng)一個答案,解空間與問題空間基本上應(yīng)該是一一對應(yīng)的。
我們給出的解方案是一個個限界上下文,一個限界上下文負(fù)責(zé)應(yīng)對其對應(yīng)的問題進(jìn)行解決,承載了該問題中的業(yè)務(wù)知識及規(guī)則。限界上下文是解決某一類問題的單一功能模塊,例如訂單限界上下文,處理開單、通知訂單狀態(tài)等一切與訂單關(guān)聯(lián)的功能。
隨著問題的劃分,將領(lǐng)域拆分成了不同的子域,每個子域有其獨(dú)有的業(yè)務(wù)知識,而這些知識需要靠一門統(tǒng)一語言來描述。而不同限界上下文間的統(tǒng)一語言是相互獨(dú)立的。可能在不同的限界上下文有同樣的名詞,但同樣的名詞在不同的限界上下文中可能表示的不是同一事物,即使是同一事物可能其在不同上下文中的關(guān)注點(diǎn)也不一致。
限界上下文之間的映射關(guān)系:
核心域是一款軟件的所能獲得的競爭優(yōu)勢的根本所在。所以在核心的建模和開發(fā)主要投入大量的時間和人力。
通用域是許多大型軟件系統(tǒng)都有的子域。通用域例如像電子郵件和短信發(fā)送、OSS、MQ等。
系統(tǒng)中的其他域被稱為支撐域。支撐域的主要作用就是有助于支撐核心域的業(yè)務(wù)。比如:用戶的注冊等。
DDD分析建模主要有兩種方法可以使用:用例分析法、四色建模法、事件風(fēng)暴、領(lǐng)域驅(qū)動建模。
通過對需求進(jìn)行分析構(gòu)建出用例圖如下所示(舉例子不一定正確):
這里列一下標(biāo)準(zhǔn)的四色建模法(https://www.infoq.cn/article/xh-four-color-modeling/),其實還是需要多聯(lián)系才行。
時標(biāo)型(Moment-Interval)對象:具有可追溯性的記錄運(yùn)營或管理數(shù)據(jù)的時刻或時段對象,用紅色表示 -> 操作
PPT(Party/Place/Thing)對象:代表參與到流程中的參與方/地點(diǎn)/物,用綠色表示 -> 名詞
角色(Role)對象:在時標(biāo)型對象與 PPT 對象(通常是參與方)之間參與的角色,用黃色表示 -> 操作角色
描述(Description)對象:對 PPT 對象的一種補(bǔ)充描述,用藍(lán)色表示 -> 表述備注
領(lǐng)域建模并不是沒有方法,而是選擇太多,有用例法、四色建模、領(lǐng)域驅(qū)動建模、事件風(fēng)暴等,人有一個特點(diǎn)是喜歡找最簡單、最易用的方法,每個方法都有自己的特點(diǎn),并沒有好壞優(yōu)劣之分,只有適不適合。聽到一個方法就去嘗試,嘗試到一半就放棄,一種種方法嘗試,最終就會迷失在方法選擇上。
對于DDD而言領(lǐng)域建模完成后,我們就需要對照模型進(jìn)行開發(fā),在開發(fā)之間我們需要知道領(lǐng)域戰(zhàn)略設(shè)計中的幾種架構(gòu)風(fēng)格。
DDD四層架構(gòu)是一個松散分層架構(gòu)(任意上層可以調(diào)用下層,但是下層不能調(diào)用上層),DDD四層架構(gòu)的含義如下:
如下是DDD四層架構(gòu)圖:
依賴倒置原則:我們要依賴不變或穩(wěn)定的元素(類、模塊或?qū)樱橄蟛粦?yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴于抽象。依賴倒置原則可以改進(jìn)分層架構(gòu),即底層服務(wù)應(yīng)該依賴于高層組件所提供的接口。
?采用了依賴注入方式后,其實可以發(fā)現(xiàn)事實上已經(jīng)沒有分層概念了。無論高層還是底層,實際只依賴于抽象,整個分層好像被推平了,這就引入下一個架構(gòu)六邊形架構(gòu)。
六邊形架構(gòu)是Alistair Cockburn在2005年提出的,在這種架構(gòu)中,不同的客戶通過“平等”的方式與系統(tǒng)交互。在《實現(xiàn)領(lǐng)域驅(qū)動設(shè)計》一書中,作者將六邊形架構(gòu)應(yīng)用到領(lǐng)域驅(qū)動設(shè)計的實現(xiàn),六邊形的內(nèi)部代表了application和domain層。外部代表應(yīng)用的驅(qū)動邏輯、基礎(chǔ)設(shè)施或其他應(yīng)用。內(nèi)部通過端口和外部系統(tǒng)通信,端口代表了一定協(xié)議,以API呈現(xiàn)。個人理解其實就是變成內(nèi)層、適配層&外部資源,內(nèi)層是應(yīng)用層和領(lǐng)域?qū)樱贿m配層是之前的接口層和基礎(chǔ)設(shè)施層;外部資源就是對外接口、緩存、MQ、數(shù)據(jù)庫等等。六邊形架構(gòu)圖如下所示:
?下面是對六邊型架構(gòu)的一個落地實踐示例:
?
四層架構(gòu)、DIP、六邊形架構(gòu)可以參考:
https://www.jianshu.com/p/c405aa19a049
http://it.hzqiuxm.com/ddd%E9%A2%86%E5%9F%9F%E9%A9%B1%E5%8A%A8%E6%88%98%E7%95%A5%E7%AF%87%EF%BC%884%EF%BC%89/#DDD-2
在DDD設(shè)計思路中,DDD 通過領(lǐng)域?qū)ο笾g的交互實現(xiàn)業(yè)務(wù)邏輯與流程,并通過分層的方式將業(yè)務(wù)邏輯剝離出來,單獨(dú)進(jìn)行維護(hù),從而控制業(yè)務(wù)本身的復(fù)雜度。但是作為一個業(yè)務(wù)系統(tǒng),查詢的相關(guān)功能也是不可或缺的。在實現(xiàn)各式各樣的查詢功能時,往往會發(fā)現(xiàn)很難用領(lǐng)域模型來實現(xiàn)。假設(shè)在用戶需要一個訂單相關(guān)信息的查詢功能,展現(xiàn)的是查詢結(jié)果的列表。列表中的數(shù)據(jù)來自于訂單、商品、品類、送貨地址等多個領(lǐng)域?qū)ο笾械哪硯讉€字段。這樣的場景如果還是通過領(lǐng)域?qū)ο髞矸庋b就顯的很麻煩,其次與領(lǐng)域知識也沒有太緊密的關(guān)系。所以命令查詢職責(zé)分離的設(shè)計架構(gòu)(CQRS)是可以很好的解決上述的問題。
CQRS將系統(tǒng)分為兩類操作:命令(command)、查詢(Query)。命令則是對會引起數(shù)據(jù)發(fā)生變化操作的總稱,即我們常說的新增,更新,刪除這些操作,都是命令。而查詢則和字面意思一樣,即不會對數(shù)據(jù)產(chǎn)生變化的操作,只是按照某些條件查找數(shù)據(jù)。CQRS 的核心思想是將這兩類不同的操作進(jìn)行分離,然后在兩個獨(dú)立的服務(wù)中實現(xiàn)。這種獨(dú)立服務(wù)可以是兩個不同的應(yīng)用或者同一個應(yīng)用中不同的包進(jìn)行隔離。同時理論上命令和查詢的數(shù)據(jù)源也是不同的(實現(xiàn)讀寫分離)。
當(dāng)然讀寫分離模式,肯定會遇到事務(wù)的問題,事務(wù)問題主要的包含兩種情況:
所以從事務(wù)的角度來看 CQRS,你需要面對的是問題從根本來說是個最終一致性的問題。對于團(tuán)隊處理這種最終一致性問題需要有對應(yīng)的方案。
CQRS架構(gòu)如下所示:
了解了戰(zhàn)略設(shè)計后,我們主要做具體的實施,具體實施時我們需要了解實體、值對象和聚合這幾個領(lǐng)域?qū)ο蟆?/p>
我們先看一下他們的定義:
實體:許多對象不是由它們的屬性來定義,而是通過一系列的連續(xù)性(continuity)和標(biāo)識(identity)來從根本上定義的。只要一個對象在生命周期中能夠保持連續(xù)性,并且獨(dú)立于它的屬性(即使這些屬性對系統(tǒng)用戶非常重要),那它就是一個實體。
值對象:當(dāng)你只關(guān)心某個對象的屬性時,該對象便可作為一個值對象。為其添加有意義的屬性,并賦予它相應(yīng)的行為。我們需要將值對象看成不變對象,不要給它任何身份標(biāo)識,還應(yīng)該盡量避免像實體對象一樣的復(fù)雜性。
對于實體Entity,實體核心是用唯一的標(biāo)識符來定義,而不是通過屬性來定義。即即使屬性完全相同也可能是兩個不同的對象。同時實體本身有狀態(tài)的,實體又演進(jìn)的生命周期,實體本身會體現(xiàn)出相關(guān)的業(yè)務(wù)行為,業(yè)務(wù)行為會實體屬性或狀態(tài)造成影響和改變。
如果從值對象本身無狀態(tài),不可變,并且不分配具體的標(biāo)識層面來看。那么值對象可以僅僅理解為實際的Entity對象的一個屬性結(jié)合而已。該值對象附屬在一個實際的實體對象上面。值對象本身不存在一個獨(dú)立的生命周期,也一般不會產(chǎn)生獨(dú)立的行為。
對于實體和值對象詳細(xì)說明可以參考:https://www.jianshu.com/p/da51d16dbdc4
聚合是業(yè)務(wù)和邏輯緊密關(guān)聯(lián)的實體和值對象組合而成,聚合是數(shù)據(jù)修改和持久化的基本單元,一個聚合對應(yīng)一個數(shù)據(jù)的持久化;同時將選擇一個實體作為每個聚合的根,并僅允許外部對象持有對聚合根的引用。作為一個整體來定義聚合的屬性和不變量,并把其執(zhí)行責(zé)任賦予聚合根或指定的框架機(jī)制。
https://www.cnblogs.com/laozhang-is-phi/p/9916785.html
在所有的DDD項目中,通常存在多個限界上下文,這意味著我們需要找到合適的方法對這些上下文進(jìn)行集成。當(dāng)模型概念從上游上下文流入下游上下文中時, 盡量使用值對象來表示這些概念。這樣的好處是可以達(dá)到最小化集成,即可以最小化下游模型中用于管理職責(zé)的屬性數(shù)目。使用不變的值對象使得我們做更少的職責(zé)假設(shè)。
貧血模型:指使用的領(lǐng)域?qū)ο笾兄挥衧etter和getter方法(POJO),所有的業(yè)務(wù)邏輯都不包含在領(lǐng)域?qū)ο笾卸欠旁跇I(yè)務(wù)邏輯層。貧血模型中領(lǐng)域?qū)ο笾挥袑傩詻]有豐富的操作,使用時需要單獨(dú)的Dao層配合使用,風(fēng)格比較面向過程。
充血模型:將大多數(shù)業(yè)務(wù)邏輯和持久化放在領(lǐng)域?qū)ο笾校瑯I(yè)務(wù)邏輯只是完成對業(yè)務(wù)邏輯的封裝、事務(wù)和權(quán)限等的處理。充血模型更加傾向于面向?qū)ο蟮脑O(shè)計風(fēng)格(可以參考:https://www.jianshu.com/p/fae3da337ebc)。
模塊(Module)是DDD中明確提到的一種控制限界上下文的手段,在我們的工程中,一般盡量用一個模塊來表示一個領(lǐng)域的限界上下文。一般的工程中包的組織方式為{com.公司名.組織架構(gòu).業(yè)務(wù).上下文.*},這樣的組織結(jié)構(gòu)能夠明確的將一個上下文限定在包的內(nèi)部。
領(lǐng)域服務(wù):領(lǐng)域中的服務(wù)表示一個無狀態(tài)的操作,它用于實現(xiàn)特定于某個領(lǐng)域的任務(wù)。
工廠:在DDD中有可能存在復(fù)雜對象的創(chuàng)建,如果使用對象本身進(jìn)行創(chuàng)建有可能會破壞單一責(zé)任原則,所以需要工廠進(jìn)行對象創(chuàng)建。
概念
SGML稱為:標(biāo)準(zhǔn)通用標(biāo)記語言。
定義電子文檔結(jié)構(gòu)和描述其內(nèi)容的國際標(biāo)準(zhǔn)語言。
特點(diǎn)
1:通用標(biāo)言為語法置標(biāo)提供了異常強(qiáng)大的工具,同時具有極好的擴(kuò)展性,因此在數(shù)據(jù)分類和索引中非常有用;
2:是所有 電子文檔標(biāo)記語言的起源,早在萬維網(wǎng)發(fā)明之前“通用標(biāo)言”就已存在。
只描述文檔標(biāo)記應(yīng)該是怎么樣的元語言。
總結(jié):HTML是SGML的一個實例,它的DTD作為標(biāo)準(zhǔn)被固定下來,因此,HTML不能作為定義其它置標(biāo)語言的元語言。
概念
標(biāo)記電子文件使其具有結(jié)構(gòu)性的標(biāo)記語言,可擴(kuò)展標(biāo)記語言。
特點(diǎn)
1:結(jié)合
結(jié)合了HTML和SGML,是一種非常復(fù)雜的文檔的結(jié)構(gòu)。
2:友好
網(wǎng)站設(shè)計者可以定義自己的文檔類型。
用途
1:大量高度結(jié)構(gòu)化數(shù)據(jù)的防衛(wèi)區(qū)
2:其他各種工業(yè)領(lǐng)域,利于分類和索引。
下面是 小二哥 寫給 小二妹 的便簽,存儲為 XML:
<note>
<to>小二妹</to>
<from>小二哥</from>
<heading>Reminder</heading>
<body>記得給小伙伴們發(fā)福利哦!</body>
</note>
總結(jié):XML 被設(shè)計用來傳輸和存儲數(shù)據(jù),是擴(kuò)展標(biāo)記語言(EXtensible Markup Language),很類似 HTML。XML 的設(shè)計宗旨是傳輸數(shù)據(jù),而非顯示數(shù)據(jù),標(biāo)簽沒有被預(yù)定義。需要自行定義標(biāo)簽。具有自我描述性,是 W3C 的推薦標(biāo)準(zhǔn)。
DHTML 不是 W3C 標(biāo)準(zhǔn)。指動態(tài) HTML(Dynamic HTML)。
DHTML 不是由萬維網(wǎng)聯(lián)盟(W3C)規(guī)定的標(biāo)準(zhǔn),是一個營銷術(shù)語 - 被網(wǎng)景公司(Netscape)和微軟公司用來描述 4.x 代瀏覽器應(yīng)當(dāng)支持的新技術(shù)。
用途
DHTML 是一種用來創(chuàng)建動態(tài)站點(diǎn)的技術(shù)組合物。
總結(jié):對大多數(shù)人來說,DHTML 意味著 HTML 4.0、樣式表以及 JavaScript 的結(jié)合物,是動態(tài)的HTML。
XHTML是更嚴(yán)格更純凈的 HTML 代碼。是指可擴(kuò)展超文本標(biāo)簽語言(EXtensible HyperText Markup Language)。
XHTML是當(dāng)前HTML版的繼承者。HTML語法要求比較松散,這樣對網(wǎng)頁編寫者來說,比較方便,但對于機(jī)器來說,語言的語法越松散,處理起來就越困難,對于傳統(tǒng)的計算機(jī)來說,還有能力兼容松散語法,但對于許多其他設(shè)備,比如手機(jī),難度就比較大。因此產(chǎn)生了由DTD定義規(guī)則,語法要求更加嚴(yán)格的XHTML。
實例:
<!DOCTYPE Doctype goes here>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Title goes here</title>
</head>
<body>
</body>
</html>
總結(jié):XHTML 的目標(biāo)是取代 HTML,與 HTML 4.01 幾乎是相同的, 是更嚴(yán)格更純凈的 HTML 版本。XHTML 是作為一種 XML 應(yīng)用被重新定義的 HTML。是一個 W3C 標(biāo)準(zhǔn)。
HTML5是下一代的 HTML。將成為 HTML、XHTML 以及 HTML DOM 的新標(biāo)準(zhǔn)。
HTML 的上一個版本誕生于 1999 年。自從那以后,Web 世界已經(jīng)經(jīng)歷了巨變。
HTML5 仍處于完善之中。然而,大部分現(xiàn)代瀏覽器已經(jīng)具備了某些 HTML5 支持。
特點(diǎn)
1:語義特性(Class:Semantic)
2:本地存儲特性(Class: OFFLINE & STORAGE)
3:設(shè)備兼容特性 (Class: DEVICE ACCESS)
4:連接特性(Class: CONNECTIVITY)
5:網(wǎng)頁多媒體特性(Class: MULTIMEDIA)
6:三維、圖形及特效特性(Class: 3D, Graphics & Effects)
一個簡單的媒體播放實例
<!DOCTYPE HTML>
<html>
<body>
<video width="320" height="240" controls="controls">
<source src="kecheng.ogg" type="video/ogg">
<source src="kecheng.mp4" type="video/mp4">
你的瀏覽器不支持html5,請更換瀏覽器。
</video>
</body>
</html>
總結(jié):至于html5和html就不用多講了吧。HTML5在2007年被萬維網(wǎng)聯(lián)盟(W3C)新的工作組采用。這個工作組在2008年1月發(fā)布了HTML 5的首個公開草案。眼下,HTML5處于“最火”狀態(tài),HTML5 已成為 HTML、XHTML 以及 HTML DOM 的新標(biāo)準(zhǔn)。
上面分別介紹了SGML、XML、DHTML、XHTML、HTML5和HTML的之間的關(guān)系。順便也看看HTML吧。
概念
超文本標(biāo)記語言,標(biāo)準(zhǔn)通用標(biāo)記語言下的一個應(yīng)用。
特點(diǎn)
1:一種規(guī)范。
2:一種標(biāo)準(zhǔn)。
3:簡單但是功能強(qiáng)大,靈活方便。
4:具有極強(qiáng)的擴(kuò)展性。
5:不牽扯平臺。
實例:
<html>
<body>
<h1>這個是標(biāo)題</h1>
<p>這個是段落。</p>
</body>
</html>
總結(jié):是用來描述網(wǎng)頁的一種超文本標(biāo)記語言 (Hyper Text Markup Language), 不是一種編程語言,而是一種標(biāo)記語言 (markup language),使用標(biāo)記標(biāo)簽來描述網(wǎng)頁。
·歡迎留言評論哦~
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。