1. 程式人生 > 實用技巧 >DDD領域驅動設計實戰(七)-分層架構的“前世今生”

DDD領域驅動設計實戰(七)-分層架構的“前世今生”

點選上方“JavaEdge”,關注公眾號

設為“星標”,好文章不錯過!

整潔架構、CQRS、六邊形等微服務架構,都是為實現“高內聚低耦合”。而DDD分層架構的出現,使架構邊界變得更清晰,在微服務架構舉足輕重。

1 DDD分層架構

原則:每層僅可與其下方的層發生耦合。

分層架構也分為兩種

  1. 嚴格分層架構(Strict Layers Architecture)
    某層只能與直接位於其下方的層發生耦合

  2. 鬆散分層架構(Relaxed Layers Architecture)
    允許任意上、下方層間耦合。由於使用者介面層和應用服務通常需與基礎設施打交道,許多系統都是基於該架構

較低層有時也可和較高層耦合,但僅限於使用觀察者 (Observer)模式或者調停者(Mediator)模式的場景。

較低層絕對不能直接訪問較高層。例如,在使用調停者模式時,較高層可能實現了較低層定義的介面,然後將實現物件作為引數傳遞到較低層。當較低層呼叫該實現時, 它其實還是不知道實現出自何處。

1.1 傳統的四層架構


將領域模型和業務邏輯分離出來,並減少對基礎設施、使用者介面甚至應用層邏輯的依賴,因為它們不屬業務邏輯。將複雜系統分層,每層都應該有良好內聚性,且只依賴比較低層。

該架構下的基礎設施層位於最底層,持久化和訊息(e.g.MQ所發訊息、SMTP郵件或者SMS文字訊息)機制便處於此處。基礎設施層是應用程式的低層服務,被較高層發生耦合並複用。

1.2 優化後的四層架構

將基礎設施層放在最底層存在缺點

比如領域層中的一些技術實現是令人頭疼的。因為違背了分層原則。也很難為編寫測試。

使用依賴反轉設計原則,低層服務(比如基礎設施層)應依賴高層元件(比如使用者介面層、應用層和領域層)的介面。

  • 在使用依賴倒置原則的分層。基礎設施層在最上方實現所有其他層中定義的介面

依賴反轉支援所有層?

有人認為依賴反轉原則中只存在兩層,上層實現下層定義的抽象介面。因此上圖的基礎設施層將位於最上方,而使用者介面層、應用層和領域層應作同層、且都位於下方。

2 各層意義

2.1 使用者介面層

只用於處理使用者顯示和使用者請求,不應包含領域或業務邏輯。有人可能認為,既然使用者介面需要驗證使用者輸入,那就應該包含業務邏輯。事實上,使用者介面所進行的驗證和對領域模型的驗證不同。對那些粗製濫造且只面向領域模型的驗證行為,應該予以限制。

如果使用者介面使用了領域模型中的物件,那麼此時領域物件僅限於資料的渲染展現。在採用這種方式時,可以使用展現模型對使用者介面與領域物件進行解耦。
由於使用者可能是人,也可能是其他的系統,有時使用者介面層將採用開放主機服務的方式向外提供API。
使用者介面層是應用層的直接客戶。

使用者介面層很重要,主要前後端呼叫的適配。如果你的微服務要面向很多的應用或渠道提供服務,而每個渠道的入參和出參都不一樣,你不太可能開發出太多的應用服務,這樣Facade介面就起很好的作用了,包括DO和DTO物件的組裝和轉換等。

2.2 應用層

理論上不應有業務規則或邏輯,主要面向用例和流程相關的操作。但應用層又位於領域層之上,因為領域層包含多個聚合,所以它可以協調多個聚合的服務和領域物件完成服務編排和組合,協作完成業務操作。

應用層也是微服務間互動通道,它可呼叫其它微服務的應用服務,完成微服務之間的服務組合和編排。

設計和開發時,不要將本該放在領域層的業務邏輯放到應用層中實現。因為龐大的應用層會使領域模型失焦,時間一長你的微服務就會演化為傳統的三層架構,業務邏輯混亂。

應用服務是在應用層,它負責服務的組合、編排和轉發,負責處理業務用例的執行順序以及結果的拼裝,以粗粒度的服務通過API閘道器向前端釋出。

應用服務還可進行安全認證、許可權校驗、事務控制、傳送或訂閱領域事件等。

2.3 領域層

領域層的作用是實現企業核心業務邏輯,通過各種校驗手段保證業務的正確性。領域層主要體現領域模型的業務能力,它用來表達業務概念、業務狀態和業務規則。

領域層包含聚合根、實體、值物件、領域服務等領域模型中的領域物件。

領域模型的業務邏輯主要是由實體和領域服務來實現的,其中實體會採用充血模型來實現所有與之相關的業務功能。
實體和領域服務在實現業務邏輯上不是同級,當領域中的某些功能,單一實體(或者值物件)不能實現時,領域服務就會出馬,它可以組合聚合內的多個實體(或者值物件),實現複雜的業務邏輯。

2.4 基礎層

基礎層貫穿所有層,為其它各層提供通用的技術和基礎服務,包括第三方工具、驅動、MQ、閘道器、檔案、快取以及DB等。比較常見的功能還是提供DB持久化。

基礎層包含基礎服務,它採用依賴反轉,封裝基礎資源服務,實現應用層、領域層與基礎層的解耦,降低外部資源變化對應用的影響。

在傳統架構設計中,由於上層應用對資料庫的強耦合,很多公司在架構演進中最擔憂的可能就是換DB,因為一旦更換,就可能需重寫大部分程式碼。那採用依賴反轉設計後,應用層就可以通過解耦保持獨立核心業務邏輯。當資料庫變更時,只需更換資料庫基礎服務。

微服務架構的演進

領域模型中物件的層次從內到外依次是:值物件、實體、聚合和限界上下文。

實體或值物件的簡單變更,一般不會讓領域模型和微服務發生大變化。但聚合的重組或拆分卻可以。這是因為聚合內業務功能內聚,能獨立完成特定的業務邏輯。那聚合的重組或拆分,勢必就會引起業務模組和系統功能變化。

這裡我們可以聚合為基礎單元,完成領域模型和微服務架構的演進。聚合可以作為一個整體,在不同的領域模型之間重組或者拆分,或者直接將一個聚合獨立為微服務。

以微服務1為例,講解下微服務架構的演進過程:

  • 當你發現微服務1中聚合a的功能經常被高頻訪問,以致拖累整個微服務1效能,可把聚合a的程式碼,從微服務1中剝離,獨立為微服務2。這樣微服務2就可輕鬆應對高效能場景。

  • 在業務發展到一定程度,發現微服務3的領域模型變化,聚合d會更適合放到微服務1的領域模型中。這時你就可以將聚合d的程式碼整體搬遷到微服務1。注意定義好聚合之間的程式碼邊界

  • 最後我們發現,在經歷模型和架構演進後,微服務1已經從最初包含聚合a、b、c,演進為包含聚合b、c、d的新領域模型和微服務。

好的聚合和程式碼模型的邊界設計,可以讓你快速應對業務變化,輕鬆實現領域模型和微服務架構的演進。

微服務內服務的演進

在微服務內部,實體的方法被領域服務組合和封裝,領域服務又被應用服務組合和封裝。在服務逐層組合和封裝的過程中,你會發現這樣一個有趣的現象。

我們看下上面這張圖。在服務設計時,你並不一定能完整預測有哪些下層服務會被多少個上層服務組裝,因此領域層通常只提供一些原子服務,比如領域服務a、b、c。但隨著系統功能增強和外部接入越來越多,應用服務會不斷豐富。有一天你會發現領域服務b和c同時多次被多個應用服務呼叫了,執行順序也基本一致。這時你可以考慮將b和c合併,再將應用服務中b、c的功能下沉到領域層,演進為新的領域服務(b+c)。這樣既減少了服務的數量,也減輕了上層服務組合和編排的複雜度。

你看,這就是服務演進的過程,它是隨著你的系統發展的,最後你會發現你的領域模型會越來越精煉,越來越能適應需求的快速變化。

三層架構如何演進到DDD分層架構?

由於層間鬆耦合,我們可以專注於本層的設計,而不必關心其它層,也不必擔心自己的設計會影響其它層。可以說,DDD成功地降低了層與層之間的依賴。

其次,分層架構使得程式結構變得清晰,升級和維護更加容易。我們修改某層程式碼時,只要本層的介面引數不變,其它層可以不必修改。即使本層的介面發生變化,也隻影響相鄰的上層,修改工作量小且錯誤可以控制,不會帶來意外的風險。

那我們該怎樣轉向DDD分層架構呢?不妨看看下面這個過程。

傳統企業應用大多是單體架構,而單體架構則大多是三層架構。三層架構解決了程式內程式碼間呼叫複雜、程式碼職責不清的問題,但這種分層是邏輯概念,在物理上它是中心化的集中式架構,並不適合分散式微服務架構。

DDD分層架構中的要素其實和三層架構類似,只是在DDD分層架構中,這些要素被重新歸類,重新劃分了層,確定了層與層之間的互動規則和職責邊界。

三層架構向DDD分層架構演進,主要發生在業務邏輯層和資料訪問層。

DDD分層架構在使用者介面層引入了DTO,給前端提供了更多的可使用資料和更高的展示靈活性。

DDD分層架構對三層架構的業務邏輯層進行了更清晰的劃分,改善了三層架構核心業務邏輯混亂,程式碼改動相互影響大的情況。DDD分層架構將業務邏輯層的服務拆分到了應用層和領域層。應用層快速響應前端的變化,領域層實現領域模型的能力。

另外一個重要的變化發生在資料訪問層和基礎層之間。三層架構資料訪問採用DAO方式;DDD分層架構的資料庫等基礎資源訪問,採用了倉儲(Repository)設計模式,通過依賴倒置實現各層對基礎資源的解耦。

關於倉儲。倉儲本身屬於基礎層,但考慮到一個聚合對應一個倉儲,為了以後聚合程式碼整體遷移方便,在微服務程式碼目錄設計時,在聚合目錄下增加一個Repository的倉儲目錄,跟倉儲相關的程式碼都在這個目錄下。
這個目錄下的程式碼與聚合的其它業務程式碼是分開的。如果未來換資料庫,只需將Repository目錄下的程式碼替換。而如果聚合需要整體遷移到其它微服務中去,倉儲的程式碼也會一併遷移。

倉儲又分為兩部分:倉儲介面和倉儲實現。倉儲介面放在領域層中,倉儲實現放在基礎層。原來三層架構通用的第三方工具包、驅動、Common、Utility、Config等通用的公共的資源類統一放到了基礎層。

總結‍‍‍‍

DDD分層架構包含使用者介面層、應用層、領域層和基礎層。通過這些層次劃分,我們可以明確微服務各層的職能,劃定各領域物件的邊界,確定各領域物件的協作方式。

參考

  • 《實現領域驅動設計》

  • DDD分層架構:有效降低層與層之間的依賴

  • 《領域驅動設計》

往期推薦

DDD領域驅動實戰-子域/核心域等核心概念

實戰DDD領域驅動設計之限界上下文

DDD領域驅動設計實戰(三)- 理解實體

DDD領域驅動設計實戰(四)- 理解值物件

架構設計之高可擴充套件性

目前交流群已有800+人,旨在促進技術交流,可關注公眾號新增筆者微信邀請進群

喜歡文章,點個“在看、點贊、分享”素質三連支援一下~