DDD分層架構(gòu)的三種模式

引言

在討論DDD分層架構(gòu)的模式之前捐名,我們先一起回顧一下DDD和分層架構(gòu)的相關(guān)知識(shí)镶蹋。

DDD

DDD(Domain Driven Design成艘,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì))作為一種軟件開(kāi)發(fā)方法淆两,它可以幫助我們?cè)O(shè)計(jì)高質(zhì)量的軟件模型秋冰。在正確實(shí)現(xiàn)的情況下婶熬,我們通過(guò)DDD完成的設(shè)計(jì)恰恰就是軟件的工作方式。
UL(Ubiquitous Language暂刘,通用語(yǔ)言)是團(tuán)隊(duì)共享的語(yǔ)言谣拣,是DDD中最具威力的特性之一族展。不管你在團(tuán)隊(duì)中的角色如何苛谷,只要你是團(tuán)隊(duì)的一員腹殿,你都將使用UL例书。由于UL的重要性决采,所以需要讓每個(gè)概念在各自的上下文中是清晰無(wú)歧義的树瞭,于是DDD在戰(zhàn)略設(shè)計(jì)上提出了模式BC(Bounded Context,限界上下文)孝偎。UL和BC同時(shí)構(gòu)成了DDD的兩大支柱衣盾,并且它們是相輔相成的爷抓,即UL都有其確定的上下文含義蓝撇,而BC中的每個(gè)概念都有唯一的含義渤昌。
一個(gè)業(yè)務(wù)領(lǐng)域劃分成若干個(gè)BC传透,它們之間通過(guò)Context Map進(jìn)行集成朱盐。BC是一個(gè)顯式的邊界兵琳,領(lǐng)域模型便存在于這個(gè)邊界之內(nèi)骇径。領(lǐng)域模型是關(guān)于某個(gè)特定業(yè)務(wù)領(lǐng)域的軟件模型破衔。通常晰筛,領(lǐng)域模型通過(guò)對(duì)象模型來(lái)實(shí)現(xiàn)读第,這些對(duì)象同時(shí)包含了數(shù)據(jù)和行為,并且表達(dá)了準(zhǔn)確的業(yè)務(wù)含義父泳。
從廣義上來(lái)講惠窄,領(lǐng)域即是一個(gè)組織所做的事情以及其中所包含的一切睬捶,表示整個(gè)業(yè)務(wù)系統(tǒng)近刘。由于“領(lǐng)域模型”包含了“領(lǐng)域”這個(gè)詞觉渴,我們可能會(huì)認(rèn)為應(yīng)該為整個(gè)業(yè)務(wù)系統(tǒng)創(chuàng)建一個(gè)單一的案淋、內(nèi)聚的和全功能式的模型。然而誉碴,這并不是我們使用DDD的目標(biāo)黔帕。正好相反成黄,領(lǐng)域模型存在于BC內(nèi)奋岁。

在微服務(wù)架構(gòu)實(shí)踐中,人們大量地使用了DDD中的概念和技術(shù):

  1. 微服務(wù)中應(yīng)該首先建立UL滨攻,然后再討論領(lǐng)域模型光绕。
  2. 一個(gè)微服務(wù)最大不要超過(guò)一個(gè)BC,否則微服務(wù)內(nèi)會(huì)存在有歧義的領(lǐng)域概念漂坏。
  3. 一個(gè)微服務(wù)最小不要小于一個(gè)聚合顶别,否則會(huì)引入分布式事務(wù)的復(fù)雜度拒啰。
  4. 微服務(wù)的劃分過(guò)程類似于BC的劃分過(guò)程谋旦,每個(gè)微服務(wù)都有一個(gè)領(lǐng)域模型册着。
  5. 微服務(wù)間的集成可以通過(guò)Context Map來(lái)完成甲捏,比如ACL(Anticorruption Layer,防腐層)芒粹。
  6. 微服務(wù)間最好采用Domain Event(領(lǐng)域事件)來(lái)進(jìn)行交互化漆,使得微服務(wù)可以保持松耦合获三。
  7. ...

分層架構(gòu)

分層架構(gòu)的一個(gè)重要原則是每層只能與位于其下方的層發(fā)生耦合疙教。分層架構(gòu)可以簡(jiǎn)單分為兩種贞谓,即嚴(yán)格分層架構(gòu)和松散分層架構(gòu)。在嚴(yán)格分層架構(gòu)中祟同,某層只能與位于其直接下方的層發(fā)生耦合晕城,而在松散分層架構(gòu)中砖顷,則允許某層與它的任意下方層發(fā)生耦合滤蝠。

分層架構(gòu)的好處是顯而易見(jiàn)的授嘀。首先蹄皱,由于層間松散的耦合關(guān)系巷折,使得我們可以專注于本層的設(shè)計(jì)盔几,而不必關(guān)心其他層的設(shè)計(jì),也不必?fù)?dān)心自己的設(shè)計(jì)會(huì)影響其它層际邻,對(duì)提高軟件質(zhì)量大有裨益世曾。其次谴咸,分層架構(gòu)使得程序結(jié)構(gòu)清晰岭佳,升級(jí)和維護(hù)都變得十分容易珊随,更改某層的具體實(shí)現(xiàn)代碼叶洞,只要本層的接口保持穩(wěn)定衩辟,其他層可以不必修改艺晴。即使本層的接口發(fā)生變化财饥,也只影響相鄰的上層折晦,修改工作量小且錯(cuò)誤可以控制满着,不會(huì)帶來(lái)意外的風(fēng)險(xiǎn)风喇。
要保持程序分層架構(gòu)的優(yōu)點(diǎn)魂莫,就必須堅(jiān)持層間的松散耦合關(guān)系。設(shè)計(jì)程序時(shí)潭兽,應(yīng)先劃分出可能的層次山卦,以及此層次提供的接口和需要的接口账蓉。設(shè)計(jì)某層時(shí)铸本,應(yīng)盡量保持層間的隔離归敬,僅使用下層提供的接口汪茧。
關(guān)于分層架構(gòu)的優(yōu)點(diǎn)舱污,Martin Fowler在《Patterns of Enterprise Application Architecture》一書中給出了答案:

  1. 開(kāi)發(fā)人員可以只關(guān)注整個(gè)結(jié)構(gòu)中的某一層弥虐。
  2. 可以很容易的用新的實(shí)現(xiàn)來(lái)替換原有層次的實(shí)現(xiàn)霜瘪。
  3. 可以降低層與層之間的依賴颖对。
  4. 有利于標(biāo)準(zhǔn)化缤底。
  5. 利于各層邏輯的復(fù)用个唧。

“金無(wú)足赤徙歼,人無(wú)完人”,分層架構(gòu)也不可避免具有一些缺陷:

  1. 降低了系統(tǒng)的性能耕魄。這是顯然的吸奴,因?yàn)樵黾恿酥虚g層则奥,不過(guò)可以通過(guò)緩存機(jī)制來(lái)改善读处。
  2. 可能會(huì)導(dǎo)致級(jí)聯(lián)的修改罚舱。這種修改尤其體現(xiàn)在自上而下的方向管闷,不過(guò)可以通過(guò)依賴倒置來(lái)改善包个。

在每個(gè)BC中為了凸顯領(lǐng)域模型碧囊,DDD中提出了分層架構(gòu)模式糯而。最近幾年熄驼,筆者在實(shí)踐DDD的過(guò)程中谜洽,也經(jīng)常使用分層架構(gòu)模式,本文主要分享DDD分層架構(gòu)中比較經(jīng)典的三種模式蚌卤。

模式一:四層架構(gòu)

Eric Evans在《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)-軟件核心復(fù)雜性應(yīng)對(duì)之道》這本書中提出了傳統(tǒng)的四層架構(gòu)模式逊彭,如下圖所示:

ddd-l4.png
  1. User Interface為用戶界面層(或表示層)避矢,負(fù)責(zé)向用戶顯示信息和解釋用戶命令审胸。這里指的用戶可以是另一個(gè)計(jì)算機(jī)系統(tǒng)砂沛,不一定是使用用戶界面的人碍庵。
  2. Application為應(yīng)用層静浴,定義軟件要完成的任務(wù)马绝,并且指揮表達(dá)領(lǐng)域概念的對(duì)象來(lái)解決問(wèn)題富稻。這一層所負(fù)責(zé)的工作對(duì)業(yè)務(wù)來(lái)說(shuō)意義重大,也是與其它系統(tǒng)的應(yīng)用層進(jìn)行交互的必要渠道或杠。應(yīng)用層要盡量簡(jiǎn)單认境,不包含業(yè)務(wù)規(guī)則或者知識(shí)叉信,而只為下一層中的領(lǐng)域?qū)ο髤f(xié)調(diào)任務(wù)硼身,分配工作佳遂,使它們互相協(xié)作丑罪。它沒(méi)有反映業(yè)務(wù)情況的狀態(tài)吩屹,但是卻可以具有另外一種狀態(tài)祟峦,為用戶或程序顯示某個(gè)任務(wù)的進(jìn)度宅楞。
  3. Domain為領(lǐng)域?qū)樱ɑ蚰P蛯樱┭嵫茫?fù)責(zé)表達(dá)業(yè)務(wù)概念榕暇,業(yè)務(wù)狀態(tài)信息以及業(yè)務(wù)規(guī)則彤枢。盡管保存業(yè)務(wù)狀態(tài)的技術(shù)細(xì)節(jié)是由基礎(chǔ)設(shè)施層實(shí)現(xiàn)的缴啡,但是反映業(yè)務(wù)情況的狀態(tài)是由本層控制并且使用的。領(lǐng)域?qū)邮菢I(yè)務(wù)軟件的核心瓷们,領(lǐng)域模型位于這一層业栅。
  4. Infrastructure層為基礎(chǔ)實(shí)施層,向其他層提供通用的技術(shù)能力:為應(yīng)用層傳遞消息谬晕,為領(lǐng)域?qū)犹峁┏志没瘷C(jī)制碘裕,為用戶界面層繪制屏幕組件,等等攒钳。基礎(chǔ)設(shè)施層還能夠通過(guò)架構(gòu)框架來(lái)支持四個(gè)層次間的交互模式夕玩。

傳統(tǒng)的四層架構(gòu)都是限定型松散分層架構(gòu)你弦,即Infrastructure層的任意上層都可以訪問(wèn)該層(“L”型),而其它層遵守嚴(yán)格分層架構(gòu)

筆者在四層架構(gòu)模式的實(shí)踐中燎孟,對(duì)于分層的本地化定義主要為:

  1. User Interface層主要是Restful消息處理禽作,配置文件解析,等等揩页。
  2. Application層主要是多進(jìn)程管理及調(diào)度旷偿,多線程管理及調(diào)度戏自,多協(xié)程調(diào)度和狀態(tài)機(jī)管理烁落,等等。
  3. Domain層主要是領(lǐng)域模型的實(shí)現(xiàn)剧董,包括領(lǐng)域?qū)ο蟮拇_立兔仰,這些對(duì)象的生命周期管理及關(guān)系茫负,領(lǐng)域服務(wù)的定義,領(lǐng)域事件的發(fā)布乎赴,等等忍法。
  4. Infrastructure層主要是業(yè)務(wù)平臺(tái),編程框架榕吼,第三方庫(kù)的封裝饿序,基礎(chǔ)算法,等等羹蚣。

說(shuō)明:嚴(yán)格意義上來(lái)說(shuō)原探,User Interface指的是用戶界面,Restful消息和配置文件解析等處理應(yīng)該放在Application層顽素,User Interface層沒(méi)有的話就空缺咽弦。但User Interface也可以理解為用戶接口,所以將Restful消息和配置文件解析等處理放在User Interface層也行胁出。

模式二:五層架構(gòu)

James O. Coplien和Trygve Reenskaug在2009年發(fā)表了一篇論文《DCI架構(gòu):面向?qū)ο缶幊痰男聵?gòu)想》离唬,標(biāo)志著DCI架構(gòu)模式的誕生。有趣的是James O. Coplien也是MVC架構(gòu)模式的創(chuàng)造者划鸽,這個(gè)大叔一輩子就干了兩件事输莺,即年輕時(shí)創(chuàng)造了MVC和年老時(shí)創(chuàng)造了DCI,其他時(shí)間都在思考裸诽,讓我輩望塵莫及嫂用。
面向?qū)ο缶幊痰谋疽馐菍⒊绦騿T與用戶的視角統(tǒng)一于計(jì)算機(jī)代碼之中:對(duì)提高可用性和降低程序的理解難度來(lái)說(shuō),都是一種恩賜丈冬≈龊可是雖然對(duì)象很好地反映了結(jié)構(gòu),但在反映系統(tǒng)的動(dòng)作方面卻失敗了埂蕊,DCI的構(gòu)想是期望反映出最終用戶的認(rèn)知模型中的角色以及角色之間的交互往弓。

傳統(tǒng)上疏唾,面向?qū)ο缶幊陶Z(yǔ)言拿不出辦法去捕捉對(duì)象之間的協(xié)作,反映不了協(xié)作中往來(lái)的算法函似。就像對(duì)象的實(shí)例反映出領(lǐng)域結(jié)構(gòu)一樣槐脏,對(duì)象的協(xié)作與交互同樣是有結(jié)構(gòu)的。協(xié)作與交互也是最終用戶心智模型的組成部分撇寞,但你在代碼中找不到一個(gè)內(nèi)聚的表現(xiàn)形式去代表它們顿天。在本質(zhì)上,角色體現(xiàn)的是一般化的蔑担、抽象的算法牌废。角色沒(méi)有血肉,并不能做實(shí)際的事情啤握,歸根結(jié)底工作還是落在對(duì)象的頭上鸟缕,而對(duì)象本身還擔(dān)負(fù)著體現(xiàn)領(lǐng)域模型的責(zé)任。
人們心目中對(duì)“對(duì)象”這個(gè)統(tǒng)一的整體卻有兩種不同的模型排抬,即“系統(tǒng)是什么”和“系統(tǒng)做什么”叁扫,這就是DCI要解決的根本問(wèn)題。用戶認(rèn)知一個(gè)個(gè)對(duì)象和它們所代表的領(lǐng)域畜埋,而每個(gè)對(duì)象還必須按照用戶心目中的交互模型去實(shí)現(xiàn)一些行為莫绣,通過(guò)它在用例中所扮演的角色與其他對(duì)象聯(lián)結(jié)在一起。正因?yàn)樽罱K用戶能把兩種視角合為一體悠鞍,類的對(duì)象除了支持所屬類的成員函數(shù)对室,還可以執(zhí)行所扮演角色的成員函數(shù),就好像那些函數(shù)屬于對(duì)象本身一樣咖祭。換句話說(shuō)掩宜,我們希望把角色的邏輯注入到對(duì)象,讓這些邏輯成為對(duì)象的一部分么翰,而其地位卻絲毫不弱于對(duì)象初始化時(shí)從類所得到的方法牺汤。我們?cè)诰幾g時(shí)就為對(duì)象安排好了扮演角色時(shí)可能需要的所有邏輯。如果我們?cè)俾斆饕稽c(diǎn)浩嫌,在運(yùn)行時(shí)才知道了被分配的角色檐迟,然后注入剛好要用到的邏輯,也是可以做到的码耐。

算法及角色-對(duì)象映射由Context擁有追迟。Context“知道”在當(dāng)前用例中應(yīng)該找哪個(gè)對(duì)象去充當(dāng)實(shí)際的演員,然后負(fù)責(zé)把對(duì)象“cast”成場(chǎng)景中的相應(yīng)角色(cast 這個(gè)詞在戲劇界是選角的意思骚腥,此處的用詞至少符合該詞義敦间,另一方面的用意是聯(lián)想到cast 在某些編程語(yǔ)言類型系統(tǒng)中的含義)。在典型的實(shí)現(xiàn)里,每個(gè)用例都有其對(duì)應(yīng)的一個(gè)Context 對(duì)象廓块,而用例涉及到的每個(gè)角色在對(duì)應(yīng)的Context 里也都有一個(gè)標(biāo)識(shí)符厢绝。Context 要做的只是將角色標(biāo)識(shí)符與正確的對(duì)象綁定到一起。然后我們只要觸發(fā)Context里的“開(kāi)場(chǎng)”角色带猴,代碼就會(huì)運(yùn)行下去昔汉。

于是我們有了完整的DCI架構(gòu)(Data、Context和Interactive三層架構(gòu)):

  1. Data層描述系統(tǒng)有哪些領(lǐng)域概念及其之間的關(guān)系浓利,該層專注于領(lǐng)域?qū)ο蟮拇_立和這些對(duì)象的生命周期管理及關(guān)系挤庇,讓程序員站在對(duì)象的角度思考系統(tǒng)钞速,從而讓“系統(tǒng)是什么”更容易被理解贷掖。
  2. Context層:是盡可能薄的一層。Context往往被實(shí)現(xiàn)得無(wú)狀態(tài)渴语,只是找到合適的role苹威,讓role交互起來(lái)完成業(yè)務(wù)邏輯即可。但是簡(jiǎn)單并不代表不重要驾凶,顯示化context層正是為人去理解軟件業(yè)務(wù)流程提供切入點(diǎn)和主線牙甫。
  3. Interactive層主要體現(xiàn)在對(duì)role的建模,role是每個(gè)context中復(fù)雜的業(yè)務(wù)邏輯的真正執(zhí)行者调违,體現(xiàn)“系統(tǒng)做什么”窟哺。role所做的是對(duì)行為進(jìn)行建模,它聯(lián)接了context和領(lǐng)域?qū)ο蠹技纭S捎谙到y(tǒng)的行為是復(fù)雜且多變的且轨,role使得系統(tǒng)將穩(wěn)定的領(lǐng)域模型層和多變的系統(tǒng)行為層進(jìn)行了分離,由role專注于對(duì)系統(tǒng)行為進(jìn)行建模虚婿。該層往往關(guān)注于系統(tǒng)的可擴(kuò)展性旋奢,更加貼近于軟件工程實(shí)踐,在面向?qū)ο笾懈嗟氖且灶惖囊暯沁M(jìn)行思考設(shè)計(jì)然痊。

DCI目前廣泛被看作是對(duì)DDD的一種發(fā)展和補(bǔ)充至朗,用在基于面向?qū)ο蟮念I(lǐng)域建模上。顯式的對(duì)role進(jìn)行建模剧浸,解決了面向?qū)ο蠼V械某溲P秃拓氀P椭疇?zhēng)锹引。DCI通過(guò)顯式的用role對(duì)行為進(jìn)行建模,同時(shí)讓role在context中可以和對(duì)應(yīng)的領(lǐng)域?qū)ο筮M(jìn)行綁定(cast)唆香,從而既解決了數(shù)據(jù)邊界和行為邊界不一致的問(wèn)題粤蝎,也解決了領(lǐng)域?qū)ο笾袛?shù)據(jù)和行為高內(nèi)聚低耦合的問(wèn)題。

面向?qū)ο蠼C媾R的一個(gè)棘手問(wèn)題是數(shù)據(jù)邊界和行為邊界往往不一致袋马。遵循模塊化的思想初澎,我們通過(guò)類將行為和其緊密耦合的數(shù)據(jù)封裝在一起。但是在復(fù)雜的業(yè)務(wù)場(chǎng)景下,行為往往跨越多個(gè)領(lǐng)域?qū)ο蟊纾@樣的行為如果放在某一個(gè)對(duì)象中必然會(huì)導(dǎo)致別的對(duì)象需要向該對(duì)象暴漏其內(nèi)部狀態(tài)软啼。所以面向?qū)ο蟀l(fā)展的后來(lái),領(lǐng)域建模出現(xiàn)兩種派別之爭(zhēng)延柠,一種傾向于將跨越多個(gè)領(lǐng)域?qū)ο蟮男袨榻T陬I(lǐng)域服務(wù)中祸挪。如果這種做法使用過(guò)度,則會(huì)導(dǎo)致領(lǐng)域?qū)ο笞兂芍惶峁┮欢裧et方法的啞對(duì)象贞间,這種建模結(jié)果被稱之為貧血模型贿条。而另一派則堅(jiān)定的認(rèn)為方法應(yīng)該屬于領(lǐng)域?qū)ο螅运械臉I(yè)務(wù)行為仍然被放在領(lǐng)域?qū)ο笾性鋈龋@樣導(dǎo)致領(lǐng)域?qū)ο箅S著支持的業(yè)務(wù)場(chǎng)景變多而變成上帝類整以,而且類內(nèi)部方法的抽象層次很難一致。另外由于行為邊界很難恰當(dāng)峻仇,導(dǎo)致對(duì)象之間數(shù)據(jù)訪問(wèn)關(guān)系也比較復(fù)雜公黑,這種建模結(jié)果被稱之為充血模型。

關(guān)于多角色對(duì)象摄咆,舉個(gè)生活中的例子:

人有多重角色凡蚜,不同的角色履行的職責(zé)不同:

  1. 作為父母:我們要給孩子講故事,陪他們玩游戲吭从,哄它們睡覺(jué)朝蜘。
  2. 作為子女:我們要孝敬父母,聽(tīng)取他們的人生建議涩金。
  3. 作為下屬:我們要服從上司的工作安排谱醇,并高質(zhì)量完成任務(wù)。
  4. 作為上司:我們要安排下屬的工作鸭廷,并進(jìn)行培養(yǎng)和激勵(lì)枣抱。
  5. ...

這里人(大對(duì)象)聚合了多個(gè)角色(小類),人在某種場(chǎng)景下辆床,只能扮演特定的角色:

  1. 在孩子面前佳晶,我們是父母。
  2. 在父母面前讼载,我們是子女轿秧。
  3. 在上司面前,我們是下屬咨堤。
  4. 在下屬面前菇篡,我們是上司。
  5. ...

引入DCI后一喘,DDD四層架構(gòu)模式中的Domain層變薄了驱还,以前Domain層對(duì)應(yīng)DCI中的三層嗜暴,而現(xiàn)在:

  1. Domain層只保留了DCI中的Data層和Interaction層,我們?cè)趯?shí)踐中通常將這兩層使用目錄隔離议蟆,即通過(guò)兩個(gè)目錄object和role來(lái)分離層Data和Interaction闷沥。


    object-role-dir.png
  2. DCI中的Context層從Domain層上移變成Context層。

因此咐容,DDD分層架構(gòu)模式就變成了五層舆逃,如下圖所示:


ddd-l5.png

筆者在實(shí)踐中,將這五層的本地化定義為:

  1. User Interface是用戶接口層戳粒,主要用于處理用戶發(fā)送的Restful請(qǐng)求和解析用戶輸入的配置文件等路狮,并將信息傳遞給Application層的接口。
  2. Application層是應(yīng)用層蔚约,負(fù)責(zé)多進(jìn)程管理及調(diào)度奄妨、多線程管理及調(diào)度、多協(xié)程調(diào)度和維護(hù)業(yè)務(wù)實(shí)例的狀態(tài)模型炊琉。當(dāng)調(diào)度層收到用戶接口層的請(qǐng)求后展蒂,委托Context層與本次業(yè)務(wù)相關(guān)的上下文進(jìn)行處理又活。
  3. Context是環(huán)境層苔咪,以上下文為單位,將Domain層的領(lǐng)域?qū)ο骳ast成合適的role柳骄,讓role交互起來(lái)完成業(yè)務(wù)邏輯团赏。
  4. Domain層是領(lǐng)域?qū)樱x領(lǐng)域模型耐薯,不僅包括領(lǐng)域?qū)ο蠹捌渲g關(guān)系的建模舔清,還包括對(duì)象的角色role的顯式建模。
  5. Infrastructure層是基礎(chǔ)實(shí)施層曲初,為其他層提供通用的技術(shù)能力:業(yè)務(wù)平臺(tái)体谒,編程框架,持久化機(jī)制臼婆,消息機(jī)制抒痒,第三方庫(kù)的封裝,通用算法颁褂,等等故响。

DDD五層架構(gòu)模式討論完了嗎?故事還沒(méi)有結(jié)束...

筆者參與的很多DDD落地實(shí)踐颁独,都是面向控制面或管理面且消息交互比較多的系統(tǒng)彩届。這類系統(tǒng)的一次業(yè)務(wù),包含一組同步消息或異步消息構(gòu)成的序列誓酒,如果都放在Context層樟蠕,會(huì)導(dǎo)致該層的代碼比較復(fù)雜,于是我們考慮:

  1. Context層在面向控制面或管理面且消息交互比較多的系統(tǒng)中又分裂成兩層,即Context層和大Context層寨辩。
  2. Context層處理單位為Action寂汇,對(duì)應(yīng)一條同步消息或異步消息。
  3. 大Context層對(duì)應(yīng)一個(gè)事務(wù)處理捣染,由一個(gè)Action序列組成骄瓣,一般通過(guò)Transaction DSL實(shí)現(xiàn),所以我們習(xí)慣把大Context層叫做Transaction DSL層耍攘。
  4. Application層在面向控制面或管理面且消息交互比較多的系統(tǒng)中經(jīng)常會(huì)做一些調(diào)度相關(guān)的工作榕栏,所以我們習(xí)慣把Application層叫做Scheduler層。

因此蕾各,在面向控制面或管理面且消息交互比較多的系統(tǒng)中扒磁,DDD分層架構(gòu)模式就變成了六層,如下圖所示:

ddd-l6.png

筆者在實(shí)踐中式曲,將這六層的本地化定義為:

  1. User Interface是用戶接口層妨托,主要用于處理用戶發(fā)送的Restful請(qǐng)求和解析用戶輸入的配置文件等,并將信息傳遞給Scheduler層的接口吝羞。
  2. Scheduler是調(diào)度層兰伤,負(fù)責(zé)多進(jìn)程管理及調(diào)度、多線程管理及調(diào)度钧排、多協(xié)程調(diào)度和維護(hù)業(yè)務(wù)實(shí)例的狀態(tài)模型敦腔。當(dāng)調(diào)度層收到用戶接口層的請(qǐng)求后,委托Transaction層與本次操作相關(guān)的事務(wù)進(jìn)行處理恨溜。
  3. Transaction是事務(wù)層符衔,對(duì)應(yīng)一個(gè)業(yè)務(wù)流程,比如UE Attach糟袁,將多個(gè)同步消息或異步消息的處理序列組合成一個(gè)事務(wù)判族,而且在大多場(chǎng)景下,都有選擇結(jié)構(gòu)项戴。萬(wàn)一事務(wù)執(zhí)行失敗形帮,則立即進(jìn)行回滾。當(dāng)事務(wù)層收到調(diào)度層的請(qǐng)求后肯尺,委托Context層的Action進(jìn)行處理沃缘,常常還伴隨使用Context層的Specification(謂詞)進(jìn)行Action的選擇。
  4. Context是環(huán)境層则吟,以Action為單位槐臀,處理一條同步消息或異步消息,將Domain層的領(lǐng)域?qū)ο骳ast成合適的role氓仲,讓role交互起來(lái)完成業(yè)務(wù)邏輯水慨。環(huán)境層通常也包括Specification的實(shí)現(xiàn)得糜,即通過(guò)Domain層的知識(shí)去完成一個(gè)條件判斷。
  5. Domain層是領(lǐng)域?qū)游鳎x領(lǐng)域模型朝抖,不僅包括領(lǐng)域?qū)ο蠹捌渲g關(guān)系的建模,還包括對(duì)象的角色role的顯式建模谍珊。
  6. Infrastructure層是基礎(chǔ)實(shí)施層治宣,為其他層提供通用的技術(shù)能力:業(yè)務(wù)平臺(tái),編程框架砌滞,持久化機(jī)制侮邀,消息機(jī)制,第三方庫(kù)的封裝贝润,通用算法绊茧,等等。

事務(wù)層的核心是事務(wù)模型打掘,事務(wù)模型的框架代碼一般放在基礎(chǔ)設(shè)施層华畏。關(guān)于事務(wù)模型,筆者以前分享過(guò)一篇文章—《Golang事務(wù)模型》尊蚁,感興趣的同學(xué)可以看看亡笑。

綜上所述,DDD六層架構(gòu)可以看做是DDD五層架構(gòu)在特定領(lǐng)域的變體枝誊,我們統(tǒng)稱為DDD五層架構(gòu)况芒,而DDD五層架構(gòu)與傳統(tǒng)的四層架構(gòu)類似惜纸,都是限定型松散分層架構(gòu)叶撒。

模式三:六邊形架構(gòu)

有一種方法可以改進(jìn)分層架構(gòu),即依賴倒置原則(Dependency Inversion Principle, DIP)耐版,它通過(guò)改變不同層之間的依賴關(guān)系達(dá)到改進(jìn)目的祠够。

依賴倒置原則由Robert C. Martin提出,正式定義為:
高層模塊不應(yīng)該依賴于底層模塊粪牲,兩者都應(yīng)該依賴于抽象古瓤。
抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴于抽象腺阳。

根據(jù)該定義落君,DDD分層架構(gòu)中的低層組件應(yīng)該依賴于高層組件提供的接口,即無(wú)論高層還是低層都依賴于抽象亭引,整個(gè)分層架構(gòu)好像被推平了绎速。如果我們把分層架構(gòu)推平,再向其中加入一些對(duì)稱性焙蚓,就會(huì)出現(xiàn)一種具有對(duì)稱性特征的架構(gòu)風(fēng)格纹冤,即六邊形架構(gòu)洒宝。六邊形架構(gòu)是Alistair Cockburn在2005年提出的,在這種架構(gòu)中萌京,不同的客戶通過(guò)“平等”的方式與系統(tǒng)交互雁歌。需要新的客戶嗎?不是問(wèn)題知残。只需要添加一個(gè)新的適配器將客戶輸入轉(zhuǎn)化成能被系統(tǒng)API所理解的參數(shù)就行靠瞎。同時(shí),對(duì)于每種特定的輸出求妹,都有一個(gè)新建的適配器負(fù)責(zé)完成相應(yīng)的轉(zhuǎn)化功能较坛。

六邊形架構(gòu)也稱為端口與適配器,如下圖所示:

ddd-hex.png

六邊形每條不同的邊代表了不同類型的端口扒最,端口要么處理輸入丑勤,要么處理輸出。對(duì)于每種外界類型吧趣,都有一個(gè)適配器與之對(duì)應(yīng)法竞,外界通過(guò)應(yīng)用層API與內(nèi)部進(jìn)行交互。上圖中有3個(gè)客戶請(qǐng)求均抵達(dá)相同的輸入端口(適配器A强挫、B和C)岔霸,另一個(gè)客戶請(qǐng)求使用了適配器D。假設(shè)前3個(gè)請(qǐng)求使用了HTTP協(xié)議(瀏覽器俯渤、REST和SOAP等)呆细,而后一個(gè)請(qǐng)求使用了AMQP協(xié)議(比如RabbitMQ)。端口并沒(méi)有明確的定義八匠,它是一個(gè)非常靈活的概念絮爷。無(wú)論采用哪種方式對(duì)端口進(jìn)行劃分,當(dāng)客戶請(qǐng)求到達(dá)時(shí)梨树,都應(yīng)該有相應(yīng)的適配器對(duì)輸入進(jìn)行轉(zhuǎn)化坑夯,然后端口將調(diào)用應(yīng)用程序的某個(gè)操作或者向應(yīng)用程序發(fā)送一個(gè)事件,控制權(quán)由此交給內(nèi)部區(qū)域抡四。
應(yīng)用程序通過(guò)公共API接收客戶請(qǐng)求柜蜈,使用領(lǐng)域模型來(lái)處理請(qǐng)求。我們可以將DDD戰(zhàn)術(shù)設(shè)計(jì)的建模元素Repository的實(shí)現(xiàn)看作是持久化適配器指巡,該適配器用于訪問(wèn)先前存儲(chǔ)的聚合實(shí)例或者保存新的聚合實(shí)例淑履。正如圖中的適配器E、F和G所展示的藻雪,我們可以通過(guò)不同的方式實(shí)現(xiàn)資源庫(kù)秘噪,比如關(guān)系型數(shù)據(jù)庫(kù)、基于文檔的存儲(chǔ)阔涉、分布式緩存或內(nèi)存存儲(chǔ)等缆娃。如果應(yīng)用程序向外界發(fā)送領(lǐng)域事件消息捷绒,我們將使用適配器H進(jìn)行處理。該適配器處理消息輸出贯要,而上面提到的處理AMQP消息的適配器則是處理消息輸入的暖侨,因此應(yīng)該使用不同的端口。

我們?cè)趯?shí)際的項(xiàng)目開(kāi)發(fā)中崇渗,不同層的組件可以同時(shí)開(kāi)發(fā)字逗。當(dāng)一個(gè)組件的功能明確后,就可以立即啟動(dòng)開(kāi)發(fā)宅广。由于該組件的用戶有多個(gè)葫掉,并且這些用戶的側(cè)重點(diǎn)不同,所以需要提供多個(gè)不同的接口跟狱。同時(shí)俭厚,這些用戶的認(rèn)識(shí)也是不斷深入的,可能會(huì)多次重構(gòu)相關(guān)的接口驶臊。于是挪挤,組件的多個(gè)用戶經(jīng)常會(huì)找組件的開(kāi)發(fā)者討論這些問(wèn)題,無(wú)形中降低了組件的開(kāi)發(fā)效率关翎。
我們換一種方式扛门,組件的開(kāi)發(fā)者在明確了組件的功能后就專注于功能的開(kāi)發(fā),確保功能穩(wěn)定和高效纵寝。組件的用戶自己定義組件的接口(端口)论寨,然后基于接口寫測(cè)試,并不斷演進(jìn)接口爽茴。在跨層集成測(cè)試時(shí)葬凳,由組件開(kāi)發(fā)者或用戶再開(kāi)發(fā)一個(gè)適配器就可以了。

六邊形架構(gòu)模式的演變

盡管六邊形架構(gòu)模式已經(jīng)很好闹啦,但是沒(méi)有最好只有更好沮明,演變沒(méi)有盡頭。在六邊形架構(gòu)模式提出后的這些年窍奋,又依次衍生出三種六邊形架構(gòu)模式的變體,感興趣的讀者可以點(diǎn)擊鏈接自行學(xué)習(xí):

  1. Jeffrey Palermo在2008年提出了洋蔥架構(gòu)酱畅,六邊形架構(gòu)是洋蔥架構(gòu)的一個(gè)超集琳袄。
  2. Robert C. Martin在2012年提出了干凈架構(gòu)(Clean Architecture),這是六邊形架構(gòu)的一個(gè)變體纺酸。
  3. Russ Miles在2013年提出了Life Preserver設(shè)計(jì)窖逗,這是一種基于六邊形架構(gòu)的設(shè)計(jì)。

小結(jié)

本文先和讀者一起回顧了DDD和分層架構(gòu)的相關(guān)知識(shí)餐蔬,然后將DDD分層架構(gòu)中常用的三種模式(四層架構(gòu)碎紊、五層架構(gòu)和六邊形架構(gòu))結(jié)合實(shí)踐經(jīng)驗(yàn)分別進(jìn)行詳細(xì)闡述佑附,使得讀者深刻理解DDD分層架構(gòu)模式,以便在微服務(wù)的開(kāi)發(fā)實(shí)踐中根據(jù)具體情況選擇最合適的DDD分層架構(gòu)模式仗考,從而交付結(jié)構(gòu)清晰且易維護(hù)的軟件產(chǎn)品音同。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市秃嗜,隨后出現(xiàn)的幾起案子权均,更是在濱河造成了極大的恐慌,老刑警劉巖锅锨,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叽赊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡必搞,警方通過(guò)查閱死者的電腦和手機(jī)必指,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)恕洲,“玉大人取劫,你說(shuō)我怎么就攤上這事⊙新拢” “怎么了谱邪?”我有些...
    開(kāi)封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)庶诡。 經(jīng)常有香客問(wèn)我惦银,道長(zhǎng),這世上最難降的妖魔是什么末誓? 我笑而不...
    開(kāi)封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任扯俱,我火速辦了婚禮,結(jié)果婚禮上喇澡,老公的妹妹穿的比我還像新娘迅栅。我一直安慰自己,他們只是感情好晴玖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布读存。 她就那樣靜靜地躺著,像睡著了一般呕屎。 火紅的嫁衣襯著肌膚如雪让簿。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天秀睛,我揣著相機(jī)與錄音尔当,去河邊找鬼。 笑死蹂安,一個(gè)胖子當(dāng)著我的面吹牛椭迎,可吹牛的內(nèi)容都是我干的锐帜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼畜号,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼缴阎!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起弄兜,我...
    開(kāi)封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤药蜻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后替饿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體语泽,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年视卢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了踱卵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡据过,死狀恐怖惋砂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绳锅,我是刑警寧澤西饵,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站鳞芙,受9級(jí)特大地震影響眷柔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜原朝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一驯嘱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧喳坠,春花似錦鞠评、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至御板,卻和暖如春锥忿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怠肋。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淹朋,地道東北人笙各。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓钉答,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親杈抢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子数尿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容