1. 程式人生 > 其它 >DDD領域驅動理解

DDD領域驅動理解

在理解領域驅動的時候,網上很多大談理論的文章,這種對於初學者不是太容易接受。根據我自己的學習經歷,建議按照如下幾個步驟學習:

  1. 粗略的看一遍領域驅動的理論,做到心中有形,知道領域驅動是什麼,解決什麼問題的,大概有哪幾個模組即可。

  2. 找一個具體的專案(推薦阿里的cola4),瞭解定義了幾個module,每個module的作用是什麼的,分別負責什麼功能,瞭解每個module都依賴了其他哪些module,每個module之間是如何實現了相互呼叫的。

  3. 對照著專案中的module和領域驅動的理論,分析每個module分別對應理論中的哪塊功能,理解該module的存在意義是什麼,為什麼這樣設計,符合領域驅動中的哪個觀點。

領域驅動設計是一個軟體架構的設計理念,是一種設計思想,在程式碼結構上,並沒有所謂的“標準”可言,只要符合領域驅動的設計理念即可。對於不同的業務系統可以設計出符合該理念、適合自己系統的框架結構。

首先理解一下常見的MVC和MVCS模型。MVC是我們說的比較多的一種模型,M-資料模型,V-資料展示,C-控制層。其中M層主要包括各種資料庫實體類(entity),DAO,Mapper等和資料的儲存獲取有關的類。V層在web系統中主要就是各種頁面,html、jsp、js、css等。而對於C的話就是常見的各種Controller,用來接收前端的各種增刪改查互動,同時還負責從M層獲取資料的包裝和適配,請求引數的有效性校驗,前端請求的各種跳轉和重定向等。

  但這種嚴格的MVC在實際開發中並不怎麼用到,因為這種需要在C層做一些重複性的功能,比如AController裡面需要生成一個組織架構樹,用於頁面展示樹狀組織架構,而BController裡面有一個方法實獲取選單的樹狀結構,在這兩個controller裡面構造樹結構的程式碼是重複性的,可以封裝提取出來。假如把該方法放在AController裡面,則在BController就要引入AController,同時呼叫AController的該方法,這樣就會存在controller之間的依賴,並且controller也顯得異常臃腫龐大。

  為了解決這個問題,可以引入MVCS模型,這個模型也就是我們日常中用的最多的模型。這裡的S是指的業務服務層,把一些通用的功能方法提取到S層,用來為各個Controller提供服務。在實際的開發中,一般會為各個小模組定義自己的MVCS,一個完整的模組通常會包括:UserList.jsp、UserController、UserService、UseerDao。而多個小模組組成了整個project。

上面的modules.goods、modules.system都是在同一個idea-module(idea的模組)下,以目錄來做物理隔離的。有時候會根據情況把modules目錄下的功能模組獨立出來,當做一個project下面的獨立idea-module來處理。

  那麼什麼是領域驅動呢?在我的理解中就是把MVCS中的S層更加細粒度化,在“領域”的維度上進行拆分,實現每個領域的“高內聚低耦合”,以便達到領域之間的物理和邏輯的隔離。每個領域的業務高度內聚在一起,通過充血模型,實現自己的領域業務,同時暴露出介面供外部呼叫

  在傳統MVCS中的S層,通常會根據某個物件建立一個service,例如UserService、CompanyService。或者根據功能建立一個service,例如LoginService、MessageService。這些只是簡單的把一些可公用的方法簡單的在物理層面放在一個java檔案中,實現了簡單的物理隔離。這種維度的拆分過於微觀,是在方法粒度上進行的隔離,雖然實現了方法的共享,但是在整個service中取糅合著各種各樣的功能。例如:定義一個OrderService,裡面有訂單的新增、修改、刪除邏輯,還有訂單的結算邏輯。但從領域角度考慮,訂單的增刪改查屬於訂單管理的業務(領域),而訂單的結算屬於支付相關的業務(領域),兩個不同的業務邏輯程式碼卻在相同的service中,這樣安排會顯得邏輯混亂。而按照領域驅動的思想,把同一領域的相關操作整合在同一domain下面,且只負責與該領域相關的操作,同時暴露出功能介面供外部呼叫。領域之間可以直接相互依賴聚合。當領域內部操作需要依賴其他服務(例如修改db資料)時,可以通過自定義介面方式滿足領域內部操作,然後由其他領域外部服務實現該介面。

下面就用一個具體的領域驅動框架cola4來詳細剖析下領域驅動的設計理念。

專案示例在:https://github.com/alibaba/COLA/tree/master/samples ,可以下載後直接匯入idea。

匯入後的結構如下:

分別對應cola中的五個模組:

這裡的五個idea-module分別是client、adapter、application(app)、domain、infrastructure,五個module的依賴關係如圖所示:

  • client不依賴專案中的其他模組(圖中的cola元件不屬於專案中的模組)

  • application依賴domain和client模組,同時會通過domain間接依賴infrastructure

  • adapter直接依賴於application,同時通過application間接依賴client

  • domain不依賴任何其他模組,其內部會在gateway中定義領域閘道器介面

  • infrastructure依賴於domain,在gatewayImpl中實現領域介面

其五個模組的詳細功能如下:

  • adapter

    • 適配層,一般是充當controller,對不同的請求方(頁面/RCP)提供服務。對於未前後端分離的web系統,可以把靜態頁面放在該module下面。該層主要依賴於client層。

    • adapter依賴於client,在controller接收到請求之後,需要呼叫定義在client中的interface執行後續流程。

    • controller中接收的入參和出參也是定義在client中

  • client(facade)

    • 有些文章會把client叫做facade,其目的是用來暴露介面和定義傳遞資料的。在client裡面會定義一些interface和DTO、BO、VO等,當框架支援CQRS的時候,也會把各種event放在該層中。

    • controller呼叫的介面都會定義在該層中,同時各種入參和出參也在該層定義。

    • client層不依賴其他任何模組

  • application(facadeImpl)

    • application層實現了各種功能,供其他模組呼叫,主要是adapter中的各種controller。

    • application實現了定義在client中的各種介面,而client介面中定義的方法就是暴露出供adapter層呼叫的功能。

    • application中會定義各種executor來實現各種功能的具體邏輯

    • executor在執行業務邏輯的時候,通常會呼叫domain進行業務處理,其依賴於domain模組。如果是簡單的邏輯也會直接呼叫infrastructure層,例如一些簡單資料的儲存等

  • domains

    • 領域層主要包括領域物件(domain)、領域服務(service)、和領域閘道器(gateway)。

    • 領域物件entity不同於DO和DTO。DO(data object)是屬於資料層的物件和db表做一一對應;DTO(data transmission object)是在adapter層定義的資料傳輸物件,充當傳輸媒介。而領域物件定義在domain中,按照充血模型定義,在其內部實現各個領域的業務操作。

    • domain不依賴其他任何層,當需要呼叫其他模組服務時,則根據依賴倒置原則,在gateway裡面定義個介面,domain的業務層呼叫該介面的方法完成整個業務邏輯,而介面的實現則放在Infrastructure實現。gateway中的領域介面也可以直接供application層呼叫。

  • infrastructure

    • infrastructure為基礎服務層,主要處理和外部系統的互動。例如資料庫操作、redis操作、RPC呼叫等

    • gatewayImpl實現domain層的領域閘道器

    • 支援各種config配置

    • infrastructure中需要對領域物件轉換為DO進行操作

下面就看下example中的add請求是怎麼處理的。

  首先在adapter中定義了一個MetricsController,該controller對外提供了addATAMetric方法,同時裡面依賴了client中的com.alibaba.craftsman.api.MetricsServiceI介面。同時注意入參ATAMetricAddCmd,同樣定義在client中。

  之後找到addATAMetric的實現類是在app中,如下圖所示。同時app中又依賴於其內部定義的各個executor完成對應的操作。

  executor以元件形式定義在app中,同時呼叫domain中的領域介面,執行後續的業務操作。

  定義在domain中的領域閘道器:

  而領域閘道器的實現放在了infrastructure中:

  infrastructure接收到定義在domain中的MetricItem領域物件後,首先轉換為MetricDO物件,然後呼叫metricMapper插入資料庫中。由於這裡使用了CQRS,對資料的寫操作需要通知到讀模組,因此這裡釋出了一個MetricItemCreatedEvent,通知讀模組更新資料。

  通過上面的方式,跟著controller提供的介面,順藤摸瓜一步一步跟蹤到infrastructure層,就會對整個cola專案結構有個清晰的理解,然後再結合著領域驅動的設計思想,大概就明白了各個層級和介面的設計目的。

參考文章:

https://www.bianchengquan.com/article/539687.html

https://blog.csdn.net/significantfrank/article/details/100074716

https://www.cnblogs.com/duanxz/p/9922170.html