掃二維碼與項(xiàng)目經(jīng)理溝通
我們在微信上24小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
我在公司對支付業(yè)務(wù)、結(jié)算業(yè)務(wù)、資金業(yè)務(wù)使用DDD進(jìn)行領(lǐng)域建模的兩年,得到了許多好評,也面對過不少質(zhì)疑,總體來說還是能收獲不少,這對團(tuán)隊(duì)成員理解業(yè)務(wù)起著很大作用。近半年一直在研究DDD的落地實(shí)戰(zhàn),如今已修得階段性成果,迫不及待與大家分享我的落地經(jīng)驗(yàn)。

成都創(chuàng)新互聯(lián)公司專注于郫都網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供郫都營銷型網(wǎng)站建設(shè),郫都網(wǎng)站制作、郫都網(wǎng)頁設(shè)計(jì)、郫都網(wǎng)站官網(wǎng)定制、微信小程序定制開發(fā)服務(wù),打造郫都網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供郫都網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
DDD分為戰(zhàn)略設(shè)計(jì)與戰(zhàn)術(shù)設(shè)計(jì)。一般來說,領(lǐng)域建模是屬于戰(zhàn)略層的,而DDD工程落地是屬于戰(zhàn)術(shù)層的,兩者是否結(jié)合使用,視實(shí)際情況而定,比如傳統(tǒng)的MVC架構(gòu)也能使用DDD進(jìn)行領(lǐng)域建模,DDD架構(gòu)最好是先做DDD領(lǐng)域建模。?
最新上線的一個(gè)微服務(wù)——內(nèi)部交易中心,我們使用了DDD架構(gòu)來落地,希望看完對大家有啟發(fā)。
在工程落地之前,我們有必要先了解下主流的工程架構(gòu)或架構(gòu)思想都有哪些,對這些理論有所了解的,也可以直接跳過看下一個(gè)部分。
在該架構(gòu)中,上層模塊可以調(diào)用下層模塊,反之不行。即:
分層作用:
整潔架構(gòu)(Clean Architecture)是由Bob大叔在2012年提出的一個(gè)架構(gòu)模型,顧名思義,是為了使架構(gòu)更簡潔。
依賴規(guī)則:用一組同心圓來表示軟件的不同領(lǐng)域。一般來說,越深入代表你的軟件層次越高。外圓是戰(zhàn)術(shù)是實(shí)現(xiàn)機(jī)制,內(nèi)圓的是核心原則。
這條規(guī)則規(guī)定軟件模塊只能向內(nèi)依賴,而里面的部分對外面的模塊一無所知,也就是內(nèi)部不依賴外部,而外部依賴內(nèi)部。同樣,在外面圈中使用的數(shù)據(jù)格式不應(yīng)被內(nèi)圈中使用,特別是如果這些數(shù)據(jù)格式是由外面一圈的框架生成的。
這樣做的最大好處是當(dāng)系統(tǒng)的外部模塊不得不改變時(shí)(比如,替換已有的過時(shí)的數(shù)據(jù)庫系統(tǒng)),系統(tǒng)的內(nèi)層模塊不需要做任何改變。
六邊形架構(gòu)(Hexagonal Architecture),又叫做端口適配器模式,是由Alistair Cockburn在2005年提出的。
六邊形架構(gòu)將系統(tǒng)分為內(nèi)部(內(nèi)部六邊形)和外部,內(nèi)部代表了應(yīng)用的業(yè)務(wù)邏輯,外部代表應(yīng)用的驅(qū)動(dòng)邏輯、基礎(chǔ)設(shè)施或其他應(yīng)用。內(nèi)部通過端口和外部系統(tǒng)通信,端口代表了一定協(xié)議,以API呈現(xiàn)。
一個(gè)端口可能對應(yīng)多個(gè)外部系統(tǒng),不同的外部系統(tǒng)需要使用不同的適配器,適配器負(fù)責(zé)對協(xié)議進(jìn)行轉(zhuǎn)換。這樣就使得應(yīng)用程序能夠以一致的方式被用戶、程序、自動(dòng)化測試、批處理腳本所驅(qū)動(dòng),并且,可以在與實(shí)際運(yùn)行的設(shè)備和數(shù)據(jù)庫相隔離的情況下開發(fā)和測試。
作用于限界上下文的菱形對稱架構(gòu)從領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)分層架構(gòu)與六邊形架構(gòu)中汲取了營養(yǎng),通過對它們的融合形成了以領(lǐng)域?yàn)檩S心的內(nèi)外分層對稱結(jié)構(gòu)。
內(nèi)部以領(lǐng)域?qū)拥念I(lǐng)域模型為主,外部的網(wǎng)關(guān)層則根據(jù)方向劃分為北向網(wǎng)關(guān)與南向網(wǎng)關(guān)。通過該架構(gòu),可清晰說明整個(gè)限界上下文的組成:
限界上下文以領(lǐng)域模型為核心向南北方向?qū)ΨQ發(fā)散,從而在邊界內(nèi)形成清晰的邏輯層次,前端UI并未包含在限界上下文的邊界之內(nèi)。每個(gè)組成元素之間的協(xié)作關(guān)系表現(xiàn)了清晰直觀的自北向南的調(diào)用關(guān)系。
CQRS(Command Query Responsibility Segregation)意為命令查詢職責(zé)分離,它是一種與領(lǐng)域驅(qū)動(dòng)設(shè)計(jì) (DDD) 和事件溯源相關(guān)的架構(gòu)模式。Greg Young在2010年創(chuàng)造了這個(gè)術(shù)語,CQRS的內(nèi)容基于Bertrand Meyer的CQS設(shè)計(jì)模式。
CQRS架構(gòu)將寫入和讀取分開,它提出了單獨(dú)的 API,一個(gè)專用于更改應(yīng)用程序狀態(tài)的命令路由,另一個(gè)專用于返回有關(guān)應(yīng)用程序狀態(tài)信息的查詢路由。
基于各個(gè)架構(gòu)有其自己的優(yōu)缺點(diǎn),我們結(jié)合公司的現(xiàn)狀,取其長避其短,融合一套適合自己的架構(gòu)。
當(dāng)然,任何事物有其兩面性,融合各個(gè)框架后,也有其優(yōu)缺點(diǎn)——
優(yōu)點(diǎn):通過分離業(yè)務(wù)與技術(shù)代碼,有利于業(yè)務(wù)迭代升級(jí)維護(hù);業(yè)務(wù)驅(qū)動(dòng)而非技術(shù)/數(shù)據(jù)驅(qū)動(dòng),通過寫代碼就能積累一定的業(yè)務(wù)知識(shí);將領(lǐng)域知識(shí)和技術(shù)知識(shí)分類,從而提高代碼的可重用性。
缺點(diǎn):對從業(yè)人員業(yè)務(wù)分析能力較高,難以從經(jīng)典MVC架構(gòu)轉(zhuǎn)變過來;層級(jí)較多,寫代碼前需考慮清楚邏輯應(yīng)該寫在哪一層;規(guī)則較多,沒有MVC架構(gòu)靈活,不適用于簡單業(yè)務(wù)系統(tǒng);學(xué)習(xí)成本與轉(zhuǎn)移成本比較高,需要對DDD有更好的理解和更長的設(shè)計(jì)時(shí)間(資金組踐行DDD領(lǐng)域建模2年)。
看代碼之前我們先看下領(lǐng)域建模:
通過領(lǐng)域模型分析,內(nèi)部交易中心分為內(nèi)部調(diào)貨、規(guī)則中心、內(nèi)部出入庫、內(nèi)部銷售、內(nèi)部采購這五大模塊,每一個(gè)模塊對應(yīng)DDD就是一個(gè)聚合,所有聚合形成一個(gè)DDD的限界上下文(內(nèi)部交易上下文),之前的文章提到,限界上下文就是我們劃分微服務(wù)的一個(gè)重要依據(jù)。
接下來,我們結(jié)合DDD架構(gòu)圖與領(lǐng)域建模,看看工程代碼應(yīng)該怎么放。
基于Maven的DDD工程,頂層結(jié)構(gòu)我們按api、service劃分為兩個(gè)module。
api包的作用:
service包的作用:
此外,針對service包還有另一種主流的module劃分方式——直接把service包的api、application、domain、infrastructure作為四個(gè)獨(dú)立的module,優(yōu)點(diǎn)是能通過pom依賴的方式來限制層與層之間的依賴,開發(fā)人員能在編碼階段發(fā)現(xiàn)依賴問題及時(shí)修正,但缺點(diǎn)也明顯——不夠靈活,工程也會(huì)變得較重。
接入層又叫用戶接入層,主流用interface或api命名,基于包默認(rèn)按字母排序的原因,我建議使用“api”來命名接入層,但要注意,service包的api層與api包是不同的作用。
應(yīng)用層主要作用是業(yè)務(wù)編排、轉(zhuǎn)發(fā)、校驗(yàn)等,處理跨聚合、領(lǐng)域事件邏輯,復(fù)雜操作/復(fù)雜查詢也在此層體現(xiàn)(CQRS)。
領(lǐng)域?qū)踊蚍Q為模型層,系統(tǒng)的核心,負(fù)責(zé)表達(dá)業(yè)務(wù)概念、業(yè)務(wù)狀態(tài)信息以及業(yè)務(wù)規(guī)則,包含了該領(lǐng)域所有復(fù)雜的業(yè)務(wù)知識(shí)抽象和規(guī)則定義。
因?yàn)轭I(lǐng)域建模最終體現(xiàn)在領(lǐng)域?qū)觾?nèi),在我們建模時(shí)就要考慮領(lǐng)域?qū)拥拇a如何寫。
一些難點(diǎn):
基礎(chǔ)設(shè)施層作為工程的基礎(chǔ)設(shè)施使用,編寫與業(yè)務(wù)無關(guān)的代碼,如技術(shù)框架、工具類,此外還有一個(gè)重要的功能,要寫倉庫的實(shí)現(xiàn)類、外部服務(wù)的實(shí)現(xiàn)類。
我們再來看一下全貌:
通過實(shí)際案例,總結(jié)以下重要幾點(diǎn):
DDD工程落地考慮的是代碼的歸類劃分問題,重點(diǎn)在于業(yè)務(wù)邊界的識(shí)別、業(yè)務(wù)和技術(shù)代碼的解耦。寫代碼前需要考慮清楚不同的代碼應(yīng)該寫到哪里,結(jié)合前人優(yōu)秀的工程架構(gòu)思路與公司當(dāng)前的技術(shù)架構(gòu),整合一套靈活的、適合我們自己的DDD,不能照搬,更不能為了DDD而DDD。
其實(shí)除了常見的充血模型、貧血模型,還有不常用的失血模型、脹血模型,區(qū)別如下:
基于現(xiàn)有的Spring框架,以及個(gè)人以往的代碼編寫經(jīng)驗(yàn),在代碼落地層面還是以貧血模型進(jìn)行較恰當(dāng)。
應(yīng)用服務(wù)在應(yīng)用層,領(lǐng)域服務(wù)在領(lǐng)域?qū)樱以趺粗罉I(yè)務(wù)代碼該放哪里?
應(yīng)用服務(wù)的作用:
領(lǐng)域服務(wù)的作用:
其實(shí),難點(diǎn)在于識(shí)別業(yè)務(wù)代碼,考驗(yàn)我們對業(yè)務(wù)的理解程度與思考程度,如果可以顯然預(yù)料到未來會(huì)發(fā)生明顯的變化,則應(yīng)該在設(shè)計(jì)之初更靈活地設(shè)計(jì)好;如果對未來的變化把握并不清晰或不確定,滿足當(dāng)前業(yè)務(wù)需求即可。
我們無法避免過度設(shè)計(jì)還是設(shè)計(jì)不足,但如果架構(gòu)合理,代碼清晰,改起來成本不會(huì)特別大。這里提倡開發(fā)者盡量多與領(lǐng)域?qū)<遥I(yè)務(wù)人員或產(chǎn)品經(jīng)理)溝通,以把握代碼未來的走向。
除了常規(guī)的簡單業(yè)務(wù)代碼,還涉及到復(fù)雜業(yè)務(wù)代碼拆分到不同類的問題,最典型的是運(yùn)用設(shè)計(jì)模式。
原則上,核心邏輯在哪一層拆就放在哪一層,避免代碼散落到各處。
?DDD領(lǐng)域建模三大步:劃分邊界、統(tǒng)一語言、組織模型。
DDD工程落地四大步:整合框架思想、確定劃分思路、模型代碼映射、特殊代碼歸類。?
以上,從DDD領(lǐng)域建模到DDD工程落地實(shí)戰(zhàn)已全篇完結(jié),也歡迎大家一起來探討,如需要DDD工程的Demo也可以聯(lián)系我。
我相信80%的技術(shù)面試官都會(huì)對DDD這塊感興趣,如果你也掌握了DDD,其實(shí)就多掌握了一種面向RMB編程。

我們在微信上24小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流