1.概要
DDD(Domain-driven design,模型驅動設計)是一種軟件設計的指導思想,而非固定的一套公式化開發模板(這樣就會導致網絡上出現各種基于自己或業務上的理解而產出的DDD落地的實現,會讓很想學習的開發者迷茫)。在項目的全生命周期內,所有崗位的人員都基于對業務的相同的理解來展開工作。所有人員站在用戶的角度、業務的角度區思考問題,而不是從一開始就站在技術的角度去思考。
? ? ? ?在項目初期,需要將領域專家引入到團隊中。那什么是領域專家呢?他應該是誰?領域專家是指對業務領域非常熟悉(或在該業務領域耕耘了很多年)。他可以是團隊中的任何人,項目經理、產品經理、架構師等等都可以是領域專家,在需求分析階段大家都需要對需求整體進行評估。團隊需要思考這幾個問題,是否需要使用DDD?現有的團隊人員是否能支撐起DDD?任何技術或者設計都需要整體的評估,而不是一味地去迎合。
? ? ? ?接下來讓我們看看什么是領域驅動設計。
1.1需求分析
需求分析的方式有很多,例如:用例法,四色建模法。其目的就是為了去建立領域模型的認知。以上兩種方法可能會有些抽象,在項目前期與客戶溝通過程屬于需求分析層面該如何去做。就需要一種統一的一種規則,需要統一語言。接下來就是介紹Domain Story Telling這個概念(這個概念是由domainstorytelling.org網站提出)。包含下圖的四個部分。
由項目經理或產品經理通過這四種概念對需求進行梳理,通過這種方式可以很清楚直白的表達出業務場景。那么這四個部分各代表什么含義呢?
Pictograms and Vocabulary(象形圖和詞匯)
1.Actors(角色),(故事場景的參與者,故事指的是功能點)。
2.WorkObjects(參與者與參與者之間傳遞的內容,或需要呈現的內容稱為工作對象)。
3.Acivities(代表的Actors和WorkObjects之間關系,用線的方式來表示)。
4.Annotations(所有流程的注解)。
基于以上概念畫出來的圖大概是這樣,圖中大致描述的是電商購物的一個流程分析。
以商城購物系統為例。
(1)用戶查看商品,向購物網站發送商品列表請求信息。
(2)購物網站響應商品信息給客戶。
(3)用戶選擇商品、購買,發送購買商品請求
(4)購物網站響應訂單信息給客戶。此時訂單是未支付的。
(4.1)用戶拿到訂單信息之后,開始支付。發送支付請求。
(5)購物網站根據用戶提供的支付信息,去(銀行系統)對應的銀行賬戶中進行扣款。
(5.1)扣款成功之后,再將扣款成功的訂單信息返回給購物網站。
(6)購物網站拿到扣款成功的訂單信息之后,開始通知倉庫管理員準備發貨。(6.1)并且通知用戶,扣款成功購物網站準備發貨了。
(7)倉庫管理員拿到貨物之后打包發送給快遞員。
(8)快遞員送貨上門給客戶。
基于以上的分析,大家可以清楚的了解到商城購物的整個環節。
1.2領域分析
領域(Domain ) :一個組織做的事情(舉個比較狹隘的例子,阿里這個組織做的事情就是做電商)。
子域
核心域:解決項目核心問題,和組織業務緊密關聯。
支撐域:解決項目的非核心問題,則具有組織特性,但不具有通用性。
通用域:通用特性,沒有組織特性。
關心的業務重點不同,領域的劃分也不同。
項目初期應考慮的是如何領域建模,用業務語言去描述和構建系統。而不是用技術語言,去思考代碼怎么寫。技術是服務于業務的脫離業務談技術或者架構、設計都是空談。
(1)商品瀏覽核心域
(2)選擇購買商品、支付訂單
(3)訂單支付,銀行交互
(4)發貨、倉庫管理
(5)配送
目前我們暫時先把購物到配送整個過程劃分為五個子域,為什么不是四部分?為什么不是三部分?這個沒有明確的標準取決于分析者的當前對業務的理解,站在現有的角度去分析領域邊界。并不是說誰做就會更好,符合當前場景下的分析即可。那么劃分領域的原則是什么呢?原則是業務并不是功能,圍繞的是業務的走向劃分而不是以業務的功能點劃分。
有了領域邊界之后,就開始繪制領域的邊界圖。那么又會引出新的問題,邊界和邊界之間存在什么樣的關系?是如何交互的?
站在系統的角度從商品的查詢到最終的發貨是一個整體的流程。
(1)查詢商品的邊界和訂單的數據做交互(2)訂單需要跟支付、倉庫做交互(3)倉庫跟物流送貨做交互那么為了讓領域的內聚性更強,會需要去對領域做一個保護。保護手段有三種:防腐層(ACL)、開放主機服務(OHS)、發布語言(PL)。
防腐層(ACL)
通過適配器,橋接模式、外觀模式對于訪問操作的一種保護。查詢商品和訂購商品之間的交互是通過一個接口來實現的。定義這個接口是為了不影響別人是需要做一個處理,這個接口不會因為你的領域類的變化而影響接口的定義,我就不會去關心你的內部實現了只考慮在接口層面怎么去交互。接口的表現形式有很多種,在項目中可以是一個接口,在多個項目中可以是一個協議。例如reset api也是一種接口的實現,OSH/OL相當于就是做兩個根本不在一起的服務進行交互的一種方式。ACL可以理解為它是一種接口(Interface)層面的定義,D和U就是Down和UP用于區分上下級的關系。以查詢商品和訂購商品(訂單)舉例,如果需要做防腐層應該在哪個邊界中去做呢?答案是在查詢商品,因為不管你商品是什么樣的,對于訂單來說查詢商品屬于低層。而對于PL(訂購支付)來說就不是了,訂購支付屬于高層所以訂購商品需要主動去做防腐處理。
1.3 Domain Design
(領域建模工具,推薦使用:StarUML,或微軟的Viso)
BoundedContext(界限上下文)https://thedomaindrivendesign.io/bounded-context/
Aggregate(聚合根)
聚合根相當于領域中的一個大對象,它其實是由多個Entities和多個Value Obiect組成。它是操作具體業務流程中的關鍵點,它會由創建它的方法比如說Factories當然也可以去new。
Entities
Entities是有id的唯一標識的,有狀態的對象。比如說商城中訂單就是這樣一個概念。
Value Obiect
是一個無狀態的值對象,訂單中包含的那些數據可以看作成值對象,例如收貨信息address無論這些字段的數值如何改變都不會影響訂單的狀態而發生改變,只會影響數據不會影響狀態。
Services
在Entities滿足不了需要的情況下,它操作的都是無狀態的數據對象和邏輯。需要先考慮這兩條規則。例如轉賬功能,有AB兩個賬戶,AB都有一個Entities對象,那么誰轉賬給誰呢?如果在Entities做一個轉賬好像又不是那么合理,所以會提出一個轉賬的Services,需要傳遞兩個賬戶的Content來完成轉賬的服務。不能在Entities體現這個業務,這個業務是無狀態的,這個業務不會去影響Entities的狀態只是去做了一個業務邏輯的處理。這種情況下是可以用Services去做的。在DDD中需要弱化Services,大家不要把領域模型花了那么大心思去分析的業務,又大部分通過Services去實現成為了一個數據驅動的開發方式上。所以在做領域設計的時候大家一定要想清楚當時定義的意思和作用。
DomainEvents
因為Entities是有生命周期的且有狀態改變的,它很多業務的一些觸發條件都是因為這些狀態改變而觸發的,所以這個領域事件可以關聯很多業務的執行邏輯。例如:訂單支付完成付款之后,是需要根銀行進行交互的,當銀行將訂單數據返回給商城之后。需要觸發兩個事件,第一個通知用戶,第二個通知倉庫管理員。這兩件事情的完成都是需要基于支付完訂單之后通知給下游的兩個領域。所以我們就需要知道領域事件可以作為領域邊界觸發的一種行為,也可以立即為是解耦的一種方式。如果用領域事件的方式去實現業務那么可以拆分的更細。DomainEvents結合事務的一個分享。分布式事務里的事務消息問題,我們是把一個業務劃分為主次的,這個業務關系是相對的。以銀行扣款支付成功之后觸發兩條業務線為例子,什么是主呢?銀行的消息提醒給你已經支付成功了這個業務,這是一個主業務,那么它的延申業務有兩個(1)通知倉庫管理員去發貨(2)通知用戶支付成功。在實現業務的時,接收銀行返回支付狀態改變的時候去觸發兩個事件給延申業務。站在事務的角度上來說,需要滿足主線的業務事務提交兩個延申的業務,提交之后就不做管理了成功就成功失敗就失敗,主線就是主線延申就是延申。本地事務提交了才會發送事務消息。事務消息發送完成之后,由兩個訂閱者去完成各自的業務邏輯,如果失敗了也可以通過事務消息來做重復的事務補償。
Factories
是為了創建Entities和Aggregate的對象,Aggregate實際上也是一個Entities對象,Aggregate只是一個概念實際對應的還是一個對象。具體的實現是Entities、Value Obiect、Services、DomainEvents、Factories、Repositories。而BoundedContext、Aggregate是抽象的概念。
Repositories
代表的是數據操作的資源和方法。比如說:訂單信息最終還是要落庫的,那么就會涉及到數據庫操作那么就需要一個數據庫操作的對象。讓領域不關心數據的操作。
1.4UML(統一語言)
(看字面意思就能懂的直接略過...)
組合:兩個類的對象生命周期是關聯的。
聚合:其他業務也可能包含同樣的對象。
1.5 實踐
COLA(COLA是阿里巴巴基于領域模型實現的一套服務端的框架)
1.6 Specification introduction(規范介紹)
1.7GIT Manager(Git代碼庫版本管理)
1.8Test(測試環節)
單元測試:主要測試的是業務領域模型的邏輯。為什么要測試它呢?因為在做的時候我們的領域是不需要數據的,只要測試通過表示系統的關鍵業務沒有出問題。
性能測試:根據在設計系統的時候,需要設置指標比如系統支撐的最大上限是多少,檢測是否滿足性能指標。性能測試的前提是在一定的資源下去做的,如果不斷的增加服務器數量也是能達到性能指標的。
功能測試:大部分項目中的常見的測試,通過測試用例來驗證功能是否可用。還有集成測試和回歸測試。
2.Ref
https://domainstorytelling.org/#dst-ddd
https://www.wps.de/modeler/index.html