概述
DDD為復(fù)雜軟件的設(shè)計(jì)提供了指導(dǎo)思想捧书,其將易發(fā)生變化的業(yè)務(wù)核心域放置在限定上下文中,在確保核心域一致性和內(nèi)聚性的基礎(chǔ)上,DDD可以被多種語言和多種技術(shù)框架實(shí)現(xiàn),具體的框架實(shí)現(xiàn)需要根據(jù)實(shí)際的業(yè)務(wù)場景和需求來制定兄裂。
核心的指導(dǎo)思路歸納為:
關(guān)注點(diǎn)放在domain上句旱,將業(yè)務(wù)領(lǐng)域限定在同一上下文中
降低上下文之間的依賴阳藻,通過‘開發(fā)主機(jī)服務(wù)’(REST服務(wù)是其中的一種)、‘消息模式’谈撒、‘事件驅(qū)動(dòng)’等架構(gòu)風(fēng)格實(shí)現(xiàn)
遵循分層架構(gòu)模式
針對(duì)DDD的架構(gòu)設(shè)計(jì)腥泥,《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》提到了幾種架構(gòu)風(fēng)格:六邊形架構(gòu)、REST架構(gòu)啃匿、CQRS蛔外、事件驅(qū)動(dòng)等。在實(shí)際使用中溯乒,落地的架構(gòu)并非是純粹其中的一種夹厌,而很有可能戶將上述幾種架構(gòu)風(fēng)格結(jié)合起來實(shí)現(xiàn)。
此部分內(nèi)容主要來源于《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》的第4章裆悄,加上了自己的一些理解矛纹。
所謂的六邊形架構(gòu),其實(shí)是分層架構(gòu)的擴(kuò)展光稼,原來的分層架構(gòu)通常是上下分層的或南,比如常見的MVC模式,上層是對(duì)外的服務(wù)接口艾君,下層是對(duì)接存儲(chǔ)層或者是集成第三方服務(wù)采够,中層是業(yè)務(wù)邏輯層。我們跳出分層的概念冰垄,會(huì)發(fā)現(xiàn)上面層和下面層其實(shí)都是端口+適配器的實(shí)現(xiàn)蹬癌,上面層開放http/tcp端口,采用rest/soap/mq協(xié)議等對(duì)外提供服務(wù)虹茶,同時(shí)提供對(duì)應(yīng)協(xié)議的適配器冀瓦;下層也是端口+適配器,只不過應(yīng)用程序這時(shí)候變成了調(diào)用者写烤,第三方服務(wù)或者存儲(chǔ)層提供端口和服務(wù)翼闽,應(yīng)用程序本身實(shí)現(xiàn)適配功能。
基于上述思考洲炊,將分層接口中的上層和下層統(tǒng)一起來就變成了六邊形架構(gòu)感局,基于端口和適配器的實(shí)現(xiàn)尼啡,示意圖如下:
上圖來源于《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》的P111
我認(rèn)為六邊形架構(gòu)并非創(chuàng)造一種新的架構(gòu)風(fēng)格,只是將原來的分層架構(gòu)風(fēng)格重新解讀询微,使得架構(gòu)更加簡潔通用崖瞭。同時(shí),在DDD的設(shè)計(jì)思想下撑毛,六邊形架構(gòu)風(fēng)格书聚,讓領(lǐng)域模型處于架構(gòu)的核心區(qū)域,讓開發(fā)人員將焦點(diǎn)聚集到領(lǐng)域藻雌。DDD和六邊形架構(gòu)是天然契合的雌续,是DDD的首選架構(gòu)。
REST——即Representational State Transfer的縮寫胯杭,翻譯過來是"表現(xiàn)層狀態(tài)轉(zhuǎn)化"驯杜。參考至:理解RESTful架構(gòu)。
RESTful風(fēng)格的架構(gòu)將‘資源’放在第一位做个,每個(gè)‘資源’都有一個(gè)URI與之對(duì)應(yīng)鸽心,可以將‘資源’看著是ddd中的實(shí)體;RESTful采用具有自描述功能的消息實(shí)現(xiàn)無狀態(tài)通信居暖,提高系統(tǒng)的可用性顽频;至于‘資源’的哪些屬性可以公開出去,針對(duì)‘資源’的操作太闺,RESTful使用HTTP協(xié)議的已有方法來實(shí)現(xiàn):GET糯景、PUT、POST和DELETE跟束。
在DDD的實(shí)現(xiàn)中莺奸,我們可以將對(duì)外的服務(wù)設(shè)計(jì)為RESTful風(fēng)格的服務(wù),將實(shí)體/值對(duì)象/領(lǐng)域服務(wù)作為'資源'對(duì)外提供增刪改查服務(wù)冀宴。但是并不建議直接將實(shí)體暴露在外灭贷,一來實(shí)體的某些隱私屬性并不能對(duì)外暴露,二來某些資源獲取場景并不是一個(gè)實(shí)體就能滿足的略贮,因此我們在實(shí)際實(shí)踐過程中甚疟,在領(lǐng)域模型上增加了dto這樣一個(gè)角色,dto可以組合多個(gè)實(shí)體/值對(duì)象的資源對(duì)外暴露逃延。
CQRS——Cammand-Query Responsibility Segregation的縮寫览妖。翻譯過來就是“命令與查詢職責(zé)分離”。
簡而言之揽祥,CQRS就是平常大家在講的讀寫分離讽膏,通常讀寫分離的目的是為了提高查詢性能,同時(shí)達(dá)到讀/寫的解耦拄丰。讓DDD和CQRS結(jié)合府树,我們可以分別對(duì)讀和寫建模俐末,查詢模型通常是一種非規(guī)范化數(shù)據(jù)模型,它并不反映領(lǐng)域行為奄侠,只是用于數(shù)據(jù)顯示卓箫;命令模型執(zhí)行領(lǐng)域行為,且在領(lǐng)域行為執(zhí)行完成后垄潮,想辦法通知到查詢模型烹卒。
那么命令模型如何通知到查詢模型呢? 如果查詢模型和領(lǐng)域模型共享數(shù)據(jù)源弯洗,則可以省卻這一步旅急;如果沒有共用數(shù)據(jù)源,則可以借助于‘消息模式’(Messaging Patterns)通知到查詢模型涂召,從而達(dá)到最終一致性(Eventual Consistency)坠非。
Martin在blog中指出:CQRS適用于極少數(shù)復(fù)雜的業(yè)務(wù)領(lǐng)域敏沉,如果不是很適合反而會(huì)增加復(fù)雜度果正;另一個(gè)適用場景為獲取高性能的服務(wù)。
圖片來源于Martin Fowler的blog盟迟,圖中表述的查詢模型和命令模型共用數(shù)據(jù)源秋泳。
關(guān)于CQRS的討論可以參考Martin大叔的blog:CQRS,以及:CQRS, Task Based UIs, Event Sourcing agh!
?這一架構(gòu)風(fēng)格在實(shí)際項(xiàng)目中并未使用攒菠,不做過多闡述迫皱,感興趣的同學(xué)自行研究。
結(jié)合最近在重構(gòu)的社區(qū)服務(wù)系統(tǒng)(ECO)辖众,嘗試使用上述的指導(dǎo)思想和架構(gòu)風(fēng)格卓起,完成一次架構(gòu)設(shè)計(jì)嘗試,并詳述如下:
ECO系統(tǒng)架構(gòu)整合了六邊形架構(gòu)凹炸、RESTful架構(gòu)風(fēng)格戏阅、CQRS架構(gòu)風(fēng)格三種架構(gòu)風(fēng)格,并遵循經(jīng)典的分層架構(gòu)思想啤它。
1奕筐、在遵循分層架構(gòu)思想的基礎(chǔ)上,引入了六邊形架構(gòu)風(fēng)格变骡,對(duì)內(nèi)對(duì)外均通過適配器+端口的方式呈現(xiàn):
面向用戶側(cè)离赫,提供http端口,并使用SpringMVC框架的RequestMapping塌碌、Controller等組件實(shí)現(xiàn)對(duì)http 請(qǐng)求的解析渊胸,轉(zhuǎn)化為Application層可識(shí)別的業(yè)務(wù)dto對(duì)象,這里的Controller+RequestMapping便起著適配器的作用台妆;
面向第三方服務(wù)翎猛,通過httpclient和tcpclient的適配瓢捉,可以對(duì)接多種協(xié)議的第三方服務(wù)端口;
面向存儲(chǔ)層办成,通過mongoclient的適配泡态,訪問mongodb;通過mybatis的適配迂卢,訪問oracle某弦;通過redisclient的適配,訪問redis而克;
面向消息中間件靶壮,通過mqclient的適配,方為rabbitMQ员萍;
2腾降、實(shí)現(xiàn)了RESTful架構(gòu)風(fēng)格,通過RESTful風(fēng)格的接口契約對(duì)外提供主機(jī)開放服務(wù)碎绎。借助SpringMVC實(shí)現(xiàn)螃壤。
3、實(shí)現(xiàn)了CQRS架構(gòu)風(fēng)格:
orcale作為命令模型存儲(chǔ)存在筋帖,并配以Transaction事務(wù)管理奸晴。主要存儲(chǔ)帖子、評(píng)論日麸、話題寄啼、圈子、關(guān)注等實(shí)體信息代箭;
Mongodb作為查詢模型存儲(chǔ)存在墩划,存儲(chǔ)個(gè)人動(dòng)態(tài)、社區(qū)動(dòng)態(tài)等非結(jié)構(gòu)化數(shù)據(jù)嗡综;
redis同樣作為查詢模型存儲(chǔ)存在乙帮,存儲(chǔ)用戶個(gè)人信息、熱門評(píng)論蛤高、熱門帖子蚣旱、熱門話題、用戶點(diǎn)贊信息等戴陡;
Application層分為QueryService和CommandService兩大類應(yīng)用服務(wù)塞绿,分別組合查詢模型和命令模型;
使用rabbitMQ作為消息中間件恤批,CommandService在完成命令模型的維護(hù)后异吻,生產(chǎn)事件消息寫入rabbitMQ,QueryService作為消費(fèi)者從rabbitMQ讀取事件消息,更新查詢模型诀浪;
查詢模型和命令模型極其對(duì)應(yīng)的application service可以獨(dú)立部署棋返,獨(dú)立擴(kuò)展。
4雷猪、遵循基本的分層架構(gòu)風(fēng)格睛竣。
User Interface —— 用戶接口層。對(duì)外提供各種協(xié)議形式的服務(wù)求摇,并提供Validation參數(shù)校驗(yàn)射沟,authenticate權(quán)限認(rèn)證,業(yè)務(wù)實(shí)體組裝器Assembler等与境。圖中標(biāo)綠組件验夯。
Application —— 應(yīng)用服務(wù)層。組合多個(gè)業(yè)務(wù)實(shí)體摔刁、基礎(chǔ)設(shè)施層的各種組件完成業(yè)務(wù)服務(wù)挥转。圖中標(biāo)黃部分。
Domain —— 業(yè)務(wù)領(lǐng)域?qū)庸睬DD概念中的核心業(yè)務(wù)層绑谣,封裝所有業(yè)務(wù)邏輯,包含entity趁俊、value object域仇、domain service刑然、domain event等寺擂。圖中標(biāo)藍(lán)部分。
Infrastructure —— 基礎(chǔ)設(shè)施層泼掠。提供公共組件怔软,如:Logging、Trascation择镇、HttpClient等挡逼。圖中標(biāo)灰部分。