DDD分層架構(gòu):有效降低層與層之間的依賴
什么是 DDD 分層架構(gòu)?
DDD(領(lǐng)域驅(qū)動設(shè)計)的分層架構(gòu)經(jīng)歷了持續(xù)的演進。最初是經(jīng)典的四層架構(gòu);隨后,四層架構(gòu)得到了進一步優(yōu)化,實現(xiàn)了各層與基礎(chǔ)設(shè)施層的解耦;再到后來,在領(lǐng)域?qū)雍蛻?yīng)用層之間引入了上下文環(huán)境(Context)層,從而形成了五層架構(gòu)(DCI,Data-Context-Interaction)
圖片
讓我們來看一下這張圖。在最早的傳統(tǒng)四層架構(gòu)中,基礎(chǔ)層(Infrastructure Layer)被其他層所依賴,位于架構(gòu)的核心位置。然而,按照分層架構(gòu)的設(shè)計思想,領(lǐng)域?qū)樱―omain Layer)才是軟件的核心,因此這種依賴關(guān)系顯然存在問題。為了解決這一問題,我們引入了依賴倒置原則(Dependency Inversion Principle, DIP),對傳統(tǒng)的四層架構(gòu)進行了優(yōu)化,成功實現(xiàn)了各層對基礎(chǔ)層的解耦。
我們今天講的 DDD 分層架構(gòu)就是優(yōu)化后的四層架構(gòu)。在下面這張圖中,從上到下依次是:用戶接口層、應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)層。那 DDD 各層的主要職責是什么呢?下面我來逐一介紹一下。
圖片
1. 用戶接口層
用戶接口層的主要職責是向用戶展示信息并解釋用戶指令。這里的“用戶”不僅限于人類用戶,還包括程序、自動化測試腳本以及批處理腳本等。
2. 應(yīng)用層
應(yīng)用層是一個相對較薄的層次,理論上不應(yīng)包含具體的業(yè)務(wù)規(guī)則或邏輯,而是專注于用例和流程相關(guān)的操作。它位于領(lǐng)域?qū)又希撠焻f(xié)調(diào)多個聚合的服務(wù)和領(lǐng)域?qū)ο螅瓿煞?wù)的編排和組合,從而執(zhí)行業(yè)務(wù)操作。此外,應(yīng)用層也是微服務(wù)之間交互的通道,能夠調(diào)用其他微服務(wù)的應(yīng)用服務(wù),實現(xiàn)跨微服務(wù)的服務(wù)組合和編排。
需要注意的是:在設(shè)計和開發(fā)過程中,應(yīng)避免將本應(yīng)屬于領(lǐng)域?qū)拥臉I(yè)務(wù)邏輯錯誤地放到應(yīng)用層中實現(xiàn)。如果應(yīng)用層過于龐大,會導(dǎo)致領(lǐng)域模型失去焦點,久而久之,微服務(wù)可能會退化為傳統(tǒng)的三層架構(gòu),業(yè)務(wù)邏輯變得混亂不堪。
應(yīng)用服務(wù)位于應(yīng)用層,其職責包括:
- 服務(wù)的組合、編排和轉(zhuǎn)發(fā);
- 處理業(yè)務(wù)用例的執(zhí)行順序以及結(jié)果的組裝;
- 通過 API 網(wǎng)關(guān)向前端發(fā)布粗粒度的服務(wù);
- 執(zhí)行安全認證、權(quán)限校驗、事務(wù)控制;
- 發(fā)送或訂閱領(lǐng)域事件等。
3. 領(lǐng)域?qū)?/span>
領(lǐng)域?qū)邮菍崿F(xiàn)企業(yè)核心業(yè)務(wù)邏輯的關(guān)鍵層次,通過各種校驗手段確保業(yè)務(wù)的正確性。它主要體現(xiàn)領(lǐng)域模型的業(yè)務(wù)能力,用于表達業(yè)務(wù)概念、業(yè)務(wù)狀態(tài)和業(yè)務(wù)規(guī)則。領(lǐng)域?qū)影酆细嶓w、值對象、領(lǐng)域服務(wù)等領(lǐng)域模型中的核心對象。
領(lǐng)域?qū)ο蟮年P(guān)系說明:
- 實體和領(lǐng)域服務(wù)是領(lǐng)域?qū)又袑崿F(xiàn)業(yè)務(wù)邏輯的主要組成部分。
實體通常采用充血模型,實現(xiàn)與其相關(guān)的所有業(yè)務(wù)功能。
當某些業(yè)務(wù)功能無法由單一實體(或值對象)實現(xiàn)時,領(lǐng)域服務(wù)會介入,組合聚合內(nèi)的多個實體(或值對象),完成復(fù)雜的業(yè)務(wù)邏輯。
4. 基礎(chǔ)層
基礎(chǔ)層貫穿于所有層次,其主要職責是為其他各層提供通用的技術(shù)支持和基礎(chǔ)服務(wù)。這些服務(wù)包括但不限于:
- 第三方工具和驅(qū)動;
- 消息中間件;
- API 網(wǎng)關(guān);
- 文件存儲;
- 緩存服務(wù);
- 數(shù)據(jù)庫等。
其中,數(shù)據(jù)庫持久化是基礎(chǔ)層最常見的功能之一。
基礎(chǔ)層通過依賴倒置設(shè)計,封裝基礎(chǔ)資源服務(wù),實現(xiàn)應(yīng)用層、領(lǐng)域?qū)优c基礎(chǔ)層的解耦。這種設(shè)計能夠有效降低外部資源變化對應(yīng)用的影響。
DDD 分層架構(gòu)最重要的原則是什么?
DDD 分層架構(gòu)有一個重要的原則:每層只能與位于其下方的層發(fā)生耦合。
根據(jù)耦合的緊密程度,架構(gòu)可以分為兩種類型:嚴格分層架構(gòu)和松散分層架構(gòu)。
- 嚴格分層架構(gòu):
- 優(yōu)化后的 DDD 分層架構(gòu)模型屬于嚴格分層架構(gòu)。在這種架構(gòu)中,任何層只能依賴于其直接下方的層,依賴關(guān)系清晰且易于管理。例如:
領(lǐng)域服務(wù)只能被應(yīng)用服務(wù)調(diào)用;
應(yīng)用服務(wù)只能被用戶接口層調(diào)用。服務(wù)是逐層封裝或組合的,依賴關(guān)系明確,便于維護和擴展。
- 松散分層架構(gòu):傳統(tǒng)的 DDD 分層架構(gòu)屬于松散分層架構(gòu)。在這種架構(gòu)中,某一層可以依賴于其下方的任意層,依賴關(guān)系復(fù)雜且難以管理。例如:
領(lǐng)域服務(wù)可能同時被應(yīng)用層和用戶接口層調(diào)用;
核心業(yè)務(wù)邏輯容易外泄,增加了維護和升級的難度。
DDD 分層架構(gòu)如何推動架構(gòu)演進?
1.微服務(wù)架構(gòu)的演進
我們了解到領(lǐng)域模型中對象的層次從內(nèi)到外依次為:值對象、實體、聚合和限界上下文。
- 值對象和實體的簡單變更通常不會對領(lǐng)域模型和微服務(wù)產(chǎn)生重大影響。
- 然而,聚合的重組或拆分則可能引發(fā)較大的變化。這是因為聚合內(nèi)的業(yè)務(wù)功能是高度內(nèi)聚的,能夠獨立完成特定的業(yè)務(wù)邏輯。
當聚合發(fā)生重組或拆分時,業(yè)務(wù)模塊和系統(tǒng)功能往往會隨之發(fā)生變化。因此,我們可以以聚合為基礎(chǔ)單元,推動領(lǐng)域模型和微服務(wù)架構(gòu)的演進。
具體來說:
- 聚合的重組或拆分:聚合可以作為一個整體,在不同的領(lǐng)域模型之間進行重組或拆分。這種調(diào)整能夠更好地適應(yīng)業(yè)務(wù)需求的變化。
- 聚合獨立為微服務(wù):在某些情況下,可以直接將一個聚合獨立為一個微服務(wù)。這種方式能夠進一步提升系統(tǒng)的靈活性和可維護性。
圖片
當你發(fā)現(xiàn)微服務(wù) 1 中聚合 a 的功能經(jīng)常被高頻訪問,以致拖累整個微服務(wù) 1 的性能時,我們可以把聚合 a 的代碼,從微服務(wù) 1 中剝離出來,獨立為微服務(wù) 2。這樣微服務(wù) 2 就可輕松應(yīng)對高性能場景。
在業(yè)務(wù)發(fā)展到一定程度以后,你會發(fā)現(xiàn)微服務(wù) 3 的領(lǐng)域模型有了變化,聚合 d 會更適合放到微服務(wù) 1 的領(lǐng)域模型中。這時你就可以將聚合 d 的代碼整體搬遷到微服務(wù) 1 中。如果你在設(shè)計時已經(jīng)定義好了聚合之間的代碼邊界,這個過程不會太復(fù)雜,也不會花太多時間。
最后我們發(fā)現(xiàn),在經(jīng)歷模型和架構(gòu)演進后,微服務(wù) 1 已經(jīng)從最初包含聚合 a、b、c,演進為包含聚合 b、c、d 的新領(lǐng)域模型和微服務(wù)了。
2.微服務(wù)內(nèi)服務(wù)的演進
在微服務(wù)內(nèi)部,實體的方法被領(lǐng)域服務(wù)組合和封裝,領(lǐng)域服務(wù)又被應(yīng)用服務(wù)組合和封裝。在服務(wù)逐層組合和封裝的過程中,你會發(fā)現(xiàn)這樣一個有趣的現(xiàn)象。
圖片
讓我們來看一下這張圖。在服務(wù)設(shè)計時,你可能無法完全預(yù)測哪些下層服務(wù)會被多少個上層服務(wù)組合調(diào)用。因此,領(lǐng)域?qū)油ǔV惶峁┮恍?/span>原子服務(wù),例如領(lǐng)域服務(wù) a、b、c。
然而,隨著系統(tǒng)功能的增強和外部接入的增多,應(yīng)用服務(wù)會不斷豐富。某一天,你可能會發(fā)現(xiàn)領(lǐng)域服務(wù) b 和 c 被多個應(yīng)用服務(wù)頻繁調(diào)用,且它們的執(zhí)行順序基本一致。這時,你可以考慮將 b 和 c 合并,并將應(yīng)用服務(wù)中 b 和 c 的功能下沉到領(lǐng)域?qū)樱葑優(yōu)橐粋€新的領(lǐng)域服務(wù)(b+c)。
這種演進方式不僅減少了服務(wù)的數(shù)量,還降低了上層服務(wù)組合和編排的復(fù)雜度。
三層架構(gòu)如何演進到 DDD 分層架構(gòu)?
通過前面的講解,相信你已經(jīng)對 DDD 分層架構(gòu)的優(yōu)勢有了清晰的認識。我們可以總結(jié)出以下兩個最重要的優(yōu)點:
- 層間松耦合:DDD 分層架構(gòu)通過解耦各層之間的依賴關(guān)系,使得我們可以專注于本層的設(shè)計,而無需過多關(guān)注其他層。這種設(shè)計不僅降低了層與層之間的耦合度,還避免了因某一層的改動而影響其他層的情況。
- 結(jié)構(gòu)清晰,易于升級和維護:分層架構(gòu)使程序結(jié)構(gòu)更加清晰,升級和維護變得更加容易。當我們需要修改某一層的代碼時,只要該層的接口參數(shù)保持不變,其他層通常無需進行任何改動。即使某一層的接口發(fā)生變化,也只會影響其直接相鄰的上層,修改工作量較小且風險可控,不會引發(fā)意外的系統(tǒng)問題。
那我們該怎樣轉(zhuǎn)向 DDD 分層架構(gòu)呢?不妨看看下面這個過程。
傳統(tǒng)企業(yè)應(yīng)用大多是單體架構(gòu),而單體架構(gòu)則大多是三層架構(gòu)。三層架構(gòu)解決了程序內(nèi)代碼間調(diào)用復(fù)雜、代碼職責不清的問題,但這種分層是邏輯概念,在物理上它是中心化的集中式架構(gòu),并不適合分布式微服務(wù)架構(gòu)。
DDD 分層架構(gòu)中的要素其實和三層架構(gòu)類似,只是在 DDD 分層架構(gòu)中,這些要素被重新歸類,重新劃分了層,確定了層與層之間的交互規(guī)則和職責邊界。
圖片
讓我們來看一下這張圖,分析從三層架構(gòu)向 DDD 分層架構(gòu)演進的過程。
1. 演進的核心區(qū)域
從三層架構(gòu)向 DDD 分層架構(gòu)的演進,主要集中在業(yè)務(wù)邏輯層和數(shù)據(jù)訪問層。
2. 用戶接口層的變化
DDD 分層架構(gòu)在用戶接口層引入了 DTO(數(shù)據(jù)傳輸對象),為前端提供了更多的可用數(shù)據(jù),并提升了展示的靈活性。
3. 業(yè)務(wù)邏輯層的優(yōu)化
DDD 分層架構(gòu)對三層架構(gòu)的業(yè)務(wù)邏輯層進行了更清晰的劃分,解決了三層架構(gòu)中核心業(yè)務(wù)邏輯混亂、代碼改動相互影響大的問題。具體來說:
- 應(yīng)用層:快速響應(yīng)前端的變化,負責服務(wù)的組合、編排和轉(zhuǎn)發(fā)。
- 領(lǐng)域?qū)?/span>:實現(xiàn)領(lǐng)域模型的能力,專注于核心業(yè)務(wù)邏輯的表達。
4. 數(shù)據(jù)訪問層的改進
在數(shù)據(jù)訪問層和基礎(chǔ)層之間,DDD 分層架構(gòu)引入了倉儲(Repository)設(shè)計模式,取代了三層架構(gòu)中的 DAO 方式。倉儲模式通過依賴倒置原則,實現(xiàn)了各層對基礎(chǔ)資源的解耦。倉儲分為兩部分:
- 倉儲接口:位于領(lǐng)域?qū)樱x數(shù)據(jù)訪問的契約。
- 倉儲實現(xiàn):位于基礎(chǔ)層,負責具體的數(shù)據(jù)庫操作。
此外,三層架構(gòu)中通用的第三方工具包、驅(qū)動、Common、Utility、Config 等公共資源類,在 DDD 分層架構(gòu)中被統(tǒng)一放到了基礎(chǔ)層。
5. 演進的意義
傳統(tǒng)三層架構(gòu)向 DDD 分層架構(gòu)的演進,體現(xiàn)了領(lǐng)域驅(qū)動設(shè)計思想的逐步成熟和應(yīng)用。這種演進不僅優(yōu)化了架構(gòu)的分層設(shè)計,還提升了系統(tǒng)的靈活性、可維護性和可擴展性。