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