DDD(Domain-Driven Design)實戰


 

1,系統架構

我們有如下(微服)系統架構,在項目初期,可能因為快速上線,資源有限等條件限制,系統很可能不會做到極致的細粒度划分。

圖 1

以下我們核心圍繞Manager部分來看看,怎樣用DDD來設計分布式(微服務)應用架構。

2,DDD分布式應用架構

圖 2

如上圖所示,從相對高層的角度看,平台的領域模型設計圖,大體上可划分為3部分:1)左邊部分描述的Manager(設備管理)模塊的領域驅動模型;2)右邊部分分別是User、Resource、Control等基礎功能模塊;3)面向網關,暴露給app/web訪問的Api模塊,設計為修改/查詢分離。特別地,我們就Manager模塊的來分析設備管理相關業務的詳細領域驅動模型設計。

    模型分為4層:Domain層,Biz層(業務邏輯層),Data層(數據持久化管理層),Api層(Controller接口層)。從圖2的模型圖可以看出,其它所有層都只依賴Domain層。這就是DDD(Domain-Driven Design)的設計原則,以Domain內核為核心,各層可以獨立於具體技術實現,聚焦於業務核心需求和規范,一旦業務定義成型,后續迭代應不去(或盡可能)不修改Domian Core,以達到快速響應上層業務變化需求。在圖1(微服務架構圖)可以看到,整個大的Manager模塊划分為幾個小服務模塊:Share(共享模塊)、Device(設備模塊)和Group(分組模塊)。每一個微服務模塊又遵照各自的領域來驅動設計,同樣又可以獨立划分為:Domain層、Biz層、Data層和Api層,並獨立部署。

    然而,前期考慮業務量有限,從節省資源以及系統的穩定性和維護成本上看,圖1中Manager模塊的各個微服務,很可能共用同一個持久化存儲/緩存服務,或同一個關系型數據庫,這就完全省去了考慮分布式數據一致性問題。

    此外,從圖2也可以看到,ShareService和DeviceService,DeviceService和GroupService之間存在緊密的依賴關系,合並部署並且共享同一個數據中心,可以有效地避免分布式部署引起的數據不一致問題。最后,我們仍然會考慮到后續系統拓展性的需求,因為隨着業務的發展,無論是業務功能的增加,還是業務量的增長,都需要系統可以彈性地支持擴展。而系統以DDD的模式設計,正是能解決這樣的需求。因為我們前期已經系統內核層就已划分好Domian邊界,后續若要拆分領域實現或增加系統吞吐能力,並不會引起領域內核、接口或數據存儲的變動。

   接下來,我們給出詳細Share(共享模塊)、Device(設備模塊)和Group(分組模塊)的詳細領域驅動設計模型。

  • Share領域模型

圖 3

  • Group領域模型

圖 4

在圖5中描述了設備和分組的關系(DeviceGroup)。在本方案中,首先應該明確設備的分組關系是屬於分組管理領域邊界內的。既然是屬於分組領域邊界內的,那么將此關系移到Device領域,將隱藏以下問題:

1)在對設備進行分組管理時,會在Data層出現Group和Device之間的領域耦合,勢必導致后續領域無法拆分,擴展困難。例如,任何對設備分組關系的操作(GroupRepository#modifyDeviceGroup),都涉及對Device表的操作,導致底層數據層無法相互隔離,后續拆分困難。

2)當需要通過groupId來查詢設備信息時,是讓Group領域提供接口,還是讓Device領域提供?顯然,該關系在那個領域,就由誰提供是最方便。而把關系移到Device領域后,讓這些變得混亂不堪。

最后,顯然在Group領域定義關系(DeviceGroup),不僅易於系統擴展,也支持在基礎基礎平台提供一樣非通用(但應用很廣泛)的功能的同時,也能方便地拓展業務規則。

  • Device領域模型

圖 5

下面再看看,在具體工程里,如何實現DDD的分層應用架構。

3,DDD分層應用架構

圖 6

如第2部分描述或圖2所示,DDD應用系統分為Domain層、Biz層、Data層和Api層。明確地說,只要是遵照DDD模式設計的任何一個微服務(模塊)系統,都可分為這4層。

接下來,我們在細致的看看圖2的應用架構,涉及的Java Import和Maven依賴是如何體現的。

4,Java Import依賴

圖 7

可以看到工程有以下特點:

  • 所有層的import依賴只有Domain層
  • Biz層對(持久化或緩存)數據的操作具體實現一無所知
  • Api層對數據的檢索或業務邏輯的具體實現一無所知,只遵照接口規范暴露結果

  總之,是以Domain為核心,隔離具體實現,達到真正意義上各層之間的解耦。

5,Maven依賴

圖 8

這里我們應該會有疑問:DDD不是叫領域驅動設計嗎,為什么Api、Biz和Data層的maven依賴不僅依賴Domain,還相互之間依賴?這里一定要區別軟件解耦設計和Maven打包之間沒有任何關聯。以接口來解釋解耦,就是接口,約定了一項協議,隔離了客戶端和服務端,使得客戶端不需要關心服務端的具體實現。我們所說的軟件設計,本質是解耦設計。

下面幾點幫助理清maven依賴和Java import依賴的區別。

DDD開架構思想是圍繞領域核心來開發,所有層之間的代碼依賴都只存在於對domain的依賴。具體的:

  • 在集中式開發模式下,雖然api層的代碼只需要依賴domain層的代碼。由於domain只定義了業務接口,並沒有提供實現,要打包成一個可執行的jar包,又必須有實現來才行。但如果把biz層的實現類依賴進api層的類中,又會破壞DDD的設計原則。此時,IoC正是解決了我們的煩惱,使我們只要真正實現,只依賴接口,就可以利用IoC幫助我們解耦具體實現邏輯。
  • 當我們的業務量擴大,需要發展成分布式開發模式了。原來api層依賴的domain層的業務接口,變成了facade接口(任意rpc形式調用)。此時,maven也不再需要依賴biz層的實現類,代替的是rpc的底層技術實現。

 

6,一個困惑

在實際項目實戰時,我們會遇到一個不可避免地困惑,查詢業務依賴Service or Repository?答案是Repository。下面舉個栗子說明:

 

例如“查詢組下的設備列表”, 在api層直接調用device和group領域的repository合並數據,而不是通過在biz層合並。

1,當在集中式條件下,這種做法可以避免冗余代碼;

2,即便在后續要以分布式環境開發,domain是相互之間完全獨立不可見的,模塊之間的依賴只有facade(feign)。此時,只需要api模塊依賴device和group模塊的facade,而不是讓group模塊依賴device的facade,又讓api模塊又再次依賴group模塊facade。

 

7,一個查詢接口代碼示例(查詢組內設備列表)

圖 9

8,一個修改接口代碼示例(設備解綁)

圖 10

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM