Categories
程式開發

Golang領域模型-依賴倒置


前言:為什麼要用整篇文章來寫好像跟領域模型乾係不大的《依賴倒置》呢? 因為《依賴倒置》是六邊形架構的核心! 毫不誇張的說,不理解《依賴倒置》的程序員只能寫功能,沒法寫出框架來! 不論是依賴注入di或者依賴倒置dip,全部都是根據當前成員變量的類型,框架自動注入實例的。 區別在於成員變量是指針接收還是接口接收。 具體如何自動注入,請看《六邊形架構》和《資源庫》章節。

一、如果不進行依賴倒置會怎樣?

我們先看看什麼是依賴倒置,教科書式的解釋就是:

高層模塊不應依賴於低層模塊,二者應依賴於抽象。 抽像不應依賴於細節,細節應依賴於抽象。

我們商品領域服務需要使用Repository來持久化數據,中二代碼寫成這樣:

代碼示例

1. 資源庫具體實現基礎設施(DB)的功能

Golang領域模型-依賴倒置 1

外部資源的實現

2. 領域模型依賴資源庫

Golang領域模型-依賴倒置 2

直接依賴repository

3. 領域模型直接使用以來的資源庫實現

Golang領域模型-依賴倒置 3

使用repository的實現

這樣做的缺點是什麼?

難以維護內部(領域模型)通常是業務邏輯和策略,這裡就是DDD裡面的領域模型,一個軟件區別於其他軟件的核心就在於業務邏輯和策略實現(也就是領域模型),而外部更多的是外部資源等基礎設施。 PS:內部外部概念可參考前文—《Golang領域模型-六邊形架構》

如果領域模型依賴基礎設施,那就是業務邏輯依賴技術細節,技術細節的改變將會對業務邏輯產生影響,使其不得不改變。 這樣是不合理的。

復用困難越核心的領域模型,復用價值越高,如果對基礎設施進行依賴,那麼復用將會變得很困難。

二、如何進行依賴倒置?

計算機科學中的所有問題都可以通過引入一個間接層得到解決。 All problems in computer science can be solved by another level of indirection—— David Wheeler

實現:domain中引入dependency包定義抽象層

Golang領域模型-依賴倒置 4

dependency抽象層

代碼示例:

1. 定義了商品倉儲實現所需要滿足的接口。

Golang領域模型-依賴倒置 5

抽象層

2.商品領域服務成員變量直接引用抽象接口,框架負責依賴注入

Golang領域模型-依賴倒置 6

領域模型依賴抽象

3.商品領域模型中使用抽像出來的GoodsRepo方法

Golang領域模型-依賴倒置 7

領域模型依賴抽象

4. 外部資源具體實現抽象接口

Golang領域模型-依賴倒置 8

外部資源具體實現抽象接口

注意: var _ dependency.GoodsRepo = new(Goods) 我們用來檢查是否實現了接口

三、六邊形架構核心-依賴倒置

前面講完基礎,現在開始上大戲了!

為什麼說六邊形架構的核心是依賴倒置?

因為六邊形架構不分高低層,而分內外部,嚴格的將基礎設施和領域模型分割開來。 領域模型實現很簡單,但是將領域模型與DB,Redis,MQ等基礎設施連接起來卻很困難。

如何連接? 依賴倒置!

main函數中安裝基礎設施kafka

Golang領域模型-依賴倒置 9

阿夫卡·卡夫卡

2. 實體中抽象領域事件接口

Golang領域模型-依賴倒置 10

抽象領域事件接口

3. kafka實現領域事件接口

Golang領域模型-依賴倒置 11

實現領域事件接口

4. 訂單實體發送領域事件

Golang領域模型-依賴倒置 12

發送領域事件

依賴倒置的變與不變

通過第一、二小結概念的理解,觀察第三小結的代碼。

Kafka是個優秀的消息隊列中間件,它雖然很好,但只是基礎設施,不是系統的核心部分,也許不久的某一天我們就會把它替換掉。 亦或是替換掉別的中間件~

如果沒有依賴倒置怎麼辦? 修改業務代碼? 將所有用到過kafka的地方全部重新寫一遍? 下次有變化繼續寫? 程序員聽了想打人!

有了依賴倒置怎麼辦? 1. 新的中間件只需要實現領域事件接口。 2. 在main中重新安裝。

這就是依賴倒置的魅力,沒有什麼是不變的,重要的是將領域模型與基礎設施解耦開來。 這樣替換只需要重寫領域事件,讓領域模型保持相對穩定,不會隨著基礎設施的變化而被動變化。

四、品一品

細品以上兩種代碼,第二種實現方式中,領域模型沒有像原來一樣直接依賴外部資源,而是將依賴關係“倒置”過來,讓基礎設施去依賴由領域模型定義好的接口。

回前言所問,為什麼要用一篇文章來解釋依賴倒置,這就是六邊形的核心,外部依賴內部,內部倒置基礎設施—freedom!

總結一下:

常用的實現方式是基礎設施有自己的接口,領域模型依賴基礎設施提供的接口,比如基礎設施有自己的接口,領域模型依賴基礎設施的接口,這樣直接依賴的實現方式。

但是按照依賴倒置的原則,接口的所有權是被倒置的,表現在於接口是被領域模型的,領域模型擁有接口的所有權,基礎設施實現接口。 這樣基礎設施的改動不會影響領域模型,領域模型的複用不會依賴基礎設施。

1.依賴於構建出來的抽象,而不是具體類。 2.依賴倒置的關鍵是接口所有權的倒置。

目錄

golang領域模型-開篇golang領域模型-六邊形架構golang領域模型-實體golang領域模型-資源庫golang領域模型-依賴倒置golang領域模型-聚合根golang領域模型-CQRSgolang領域模型-領域事件

項目代碼 https://github.com/8treenet/freedom/tree/master/example/fshop

PS:加我微信:stargaze111,拉你加入DDD交流群,一起切磋DDD與代碼的藝術!