領域驅動設計(DDD)理論與方法

DDD由來與優勢

軟體架構設計的真正目的是解決軟體複雜度帶來的問題,軟體複雜度由來主要由三方面:高併發場景下的對軟體高效能要求、業務場景對軟體高可用要求、持續變化的業務以及業務擴張和增加需求對軟體擴充套件性的要求,除此外,對低成本、安全、軟體規模也一定程度上增加了軟體設計的複雜度。

在解決每個複雜度維度上,分別有各自的應對解決方案:

在高效能方面,可以透過單機和叢集兩個維度提升系統性能:在單機方面透過多程序、多執行緒等技術解決單機高併發,在叢集方面透過任務拆解、分解,將任務排程到每個Worker節點執行,從而進一步提升系統整體高併發與吞吐量

在高可用方面,可以透過冗餘服務或機器節點方式來確保整體應用的高可用,常見的有計算和儲存高可用(如Mysql主從叢集),而高可用的狀態決策是非常關鍵的一點,即是否由一個狀態轉移到另一個狀態,常見決策方式有獨裁、協商(如redis哨兵、主備切換)、民主(如Paxos、Raft等分散式一致性演算法)

在擴充套件性方面,由於軟體唯一不變就是變化這個基本定理存在,正確的預測變化、完美封裝變化,本身就是一件複雜事情;在這點上,才有把變化封裝起來,與穩定的不變內容進行隔離(分層、提升軟體內聚性,降低耦合度),不變層透過介面去適配多變的情況(如檔案系統的VFS適配多種檔案格式,同時對使用者提供標準化的操作介面場景)

在低成本、安全、規模方面,技術革命創新、功能和架構安全、軟體規模量變引發質變(微服務就是典型例子),這些因素也增加了軟體的複雜度。

然而我們往往忽略了在軟體開發階段,因為需求的理解不到位等導致的開發返工以及因為原班開發人員離職帶來的交接文件的缺失,導致新人接手專案的高上手成本、高溝通成本和高維護成本。是否存在有效的方法能夠在開發人員開始編碼之前能解決這一問題呢?

領域驅動設計(Domain Driven Desing,簡稱DDD)就是在可擴充套件性方面將複雜多變的業務排除在穩定不變的核心業務之外,從而在多變的環境中找到不變的部分,達到以不變應萬變的目標。不僅如此,DDD還能夠以更優於靜態文件的方式提供開發人員,業務人員所需要的業務知識,因為DDD在設計之初就要求業務人員特別是領域專家與開發人員透過雙方統一的語言一起來協作設計系統,最終交付給業務人員能夠“讀”得懂的,符合其期望的產品,儘管這一願望很激動人心,但實施過程並不順利,但無論如何我們也應該堅持這一目標,就像DDD作者總結的那樣,會給整個團隊甚至公司帶來的益處遠超過因為學習DDD帶來的成本,這些優勢歸結起來如是:

• 使領域專家和開發者在一起密切合作,這樣開發出來的軟體能夠準確地傳達業務規則。“準確傳達業務規則”的意思是說,此時的軟體就像是領域專家所開發出來的一樣。

• 可以幫助業務人員自我提高。沒有任何一個領域專家或者管理者敢說他對業務已經瞭如指掌了,業務知識也需要一個長期的學習過程。在DDD中,每個人都在學習,同時每個人又是知識的貢獻者。

• 關鍵在於對知識的集中,因為這樣可以確保軟體知識並不只是掌握在少數人手中。

• 在領域專家、開發者和軟體本身之間不存在“翻譯”,意思是當大家都使用相同的語言進行交流時,每人都能聽懂他人所說。

• 設計就是程式碼,程式碼就是設計。設計是關於軟體如何工作的,最好的編碼設計來自於多次試驗,這得益於敏捷的發現過程。

• DDD同時提供了戰略設計和戰術設計兩種方式。戰略設計幫助我們理解哪些投入是最重要的;哪些既有軟體資產是可以重新拿來使用的;哪些人應該被加到團隊中?戰術設計則幫助我們建立DDD模型中各個部件。

就像其他高回報率的投入一樣,DDD需要我們在時間和精力上都有所投入。但是,考慮到我們在開發軟體的過程中經常遇到的各種問題和挑戰,這樣的投入是值得的。

領域驅動設計分層架構

分層架構模式被認為是所有架構的始祖。在自然界中,大到宇宙天體,小到原子的執行模式。在人類社會中,大到國際組織,小到團體的組織模式,都採用層次結構。在軟體架構中,從早期的網路七層協議,作業系統的核心架構到廣泛使用的MVC軟體開發架構,都在使用分層架構,可見分層模式經久不衰。 分層架構的好處是顯而易見的,首先,由於層間鬆散的耦合關係,使得我們可以專注於本層的設計,而不必關心其他層的設計,也不必擔心自己的設計會影響其它層,對提高軟體質量大有裨益。其次,分層架構使得程式結構清晰,升級和維護都變得十分容易,更改某層的具體實現程式碼,只要本層的介面保持穩定,其他層可以不必修改。即使本層的介面發生變化,也隻影響相鄰的上層,修改工作量小且錯誤可以控制,不會帶來意外的風險。 分層架構的一個重要原則是每層只能與位於其下方的層發生耦合。分層架構可以簡單分為兩種,即嚴格分層架構和鬆散分層架構。在嚴格分層架構中,某層只能與位於其直接下方的層發生耦合,而在鬆散分層架構中,則允許某層與它的任意下方層發生耦合。

下圖A中所示為一個典型的DDD系統所採用的傳統分層架構,其中核心域只位於架構中的其中一層,其上為使用者介面層(User Interface)和應用層(Application Layer),其下是基礎設施層(Infrastructure Layer)。

User Interface為使用者介面層(或表示層),負責向用戶顯示資訊和解釋命令,業務邏輯僅僅是有關輸入引數驗證等的驗證等,不同於領域層的業務邏輯。這裡指的使用者可以是另一個計算機系統,不一定是使用使用者介面的人。

Application為應用層,定義軟體要完成的任務,並且指揮表達領域概念的物件來解決問題。這一層所負責的工作對業務來說意義重大,也是與其它系統的應用層進行互動的必要渠道。應用層要儘量簡單,不包含業務規則或者知識,而只為下一層中的領域物件協調任務,分配工作,使它們互相協作。 當需要建立新的聚合時,應用服務應該使用工廠或聚合的建構函式來例項化物件,然後採用資源庫對其進行持久化。應用服務還可以呼叫領域服務來完成和領域相關的任務操作,但此時的操作應該是無狀態的。當領域模型用於釋出領域事件時,應用層可以將訂閱方註冊到任意數量的事件上,這樣的好處是可以對事件進行儲存和轉發。同時,領域模型只需要關注自己的核心邏輯;領域事件釋出器也可以保持輕量化,而不用依賴於訊息機制的基礎設施。

Domain為領域層(或模型層),負責表達業務概念,業務狀態資訊以及業務規則。儘管儲存業務狀態的技術細節是由基礎設施層實現的,但是反映業務情況的狀態是由本層控制並且使用的。領域層是業務軟體的核心,領域模型位於這一層。

Infrastructure層為基礎設施層,向其他層提供通用的技術能力:為應用層傳遞訊息,為領域層提供持久化機制,為使用者介面層繪製螢幕元件等,其中訊息包含了訊息中介軟體所發的訊息、基本的電子郵件(SMTP)或者文字訊息(SMS)等

領域驅動設計(DDD)理論與方法

但是分層架構也不是沒有缺點,除了引入分層導致層之間互動帶來的效能開銷之外,也存在一些開發上的問題,比如:

1)容易導致分層劃分不當,比如領域邏輯大量滲透到應用服務層,導致領域模型變成貧血模型,儘管有些最佳實踐和其他解決方案能夠解決上述問題,但也增大了新方案帶來的學習成本

2)容易違反分層架構原則,比如在DDD設計中,如果領域層使用了基礎設施層的實體物件持久化機制,那麼基礎設施層將反向引用領域層的業務物件,違反了分層架構原則。即使領域層使用基礎設施層的持久化庫介面Repository,領域模型仍然存在外部依賴,無法保證業務邏輯的順利執行,也給測試開發帶來了困難,理想中的領域模型應該是純業務、基於記憶體執行的業務邏輯封裝。

對第一個問題,可以透過DCI來解決,詳見下文#DDD與DCI部分。對第二個問題可以透過依賴倒置原則(Dependency Inversion Principle,DIP)來解決,詳見下文#DDD與六邊形架構部分

DDD與DCI

面向物件建模面臨的一個棘手問題是資料邊界和行為邊界往往不一致。遵循模組化的思想,我們透過類將行為和其緊密耦合的資料封裝在一起。但是在複雜的業務場景下,行為往往跨越多個領域物件,這樣的行為如果放在某一個物件中必然會導致別的物件需要向該物件暴漏其內部狀態。所以面向物件發展的後來,領域建模出現兩種派別之爭,一種傾向於將跨越多個領域物件的行為建模在領域服務中。如果這種做法使用過度,則會導致領域物件變成只提供一堆get方法的啞物件,這種建模結果被稱之為貧血模型。而另一派則堅定地認為方法應該屬於領域物件,所以所有的業務行為仍然被放在領域物件中,這樣導致領域物件隨著支援的業務場景變多而變成上帝類,而且類內部方法的抽象層次很難一致。另外由於行為邊界很難恰當,導致物件之間資料訪問關係也比較複雜,這種建模結果被稱之為充血模型。

比如以字處理器中拼音檢查為例,拼音檢查這個行為功能放在哪裡?是dictionary 還是一個全域性的拼音檢查器呢?無論放在哪個物件內部,都顯得和這個物件內聚性不高,由此帶來多個呼叫拼音檢查行為物件之間的協作耦合,在DDD 中,可以使用領服務來實現;在SOA看來,拼音檢查屬於一種規則,可由規則引擎實現,透過服務整合流程和規則。

再比如銀行轉賬,如果將轉賬的業務邏輯這個程式演算法整合到賬戶Account資料模型中,因為轉賬涉及到其他賬戶和Money資料物件,那麼就將因為行為操作帶來的耦合帶到當前賬戶物件中了;當然,如果程式演算法可以精化細分,那麼我們把它切分到幾個部分,封裝成幾個物件的方法,但是這些方法都是無法表達演算法邏輯高內聚性的瑣碎小方法。

DCI架構則不同於DDD 這種有些折扣的處理方法,而是思路復位,重新考慮架構。DCI架構是James O。 Coplien和Trygve Reenskaug在2009年發表的論文《Architecture: A New Vision of Object-Oriented Programming》(以下簡稱DCI Architecture)中引入,從OO 思想根源來深入解剖DCI 對傳統面向物件的顛覆。DCI從物件資料object Data, 物件之間的協作the Collaborations between objects, 和表達需求用例中操作者角色之間的互動這三個出發點來考慮,認為資料模型data model, 角色模型role model和協作互動模型collaboration model(演算法屬於協作互動模型) 應該是程式語言核心關心點,應該從語言層面來關注,DCI各部分組成及含義如下:

Data:描述系統有哪些領域概念及其之間的關係,該層專注於領域物件的確立和這些物件的生命週期管理及關係,讓程式設計師站在物件的角度思考系統

Context:Context透過無狀態繫結role來完成業務邏輯,提供理解軟體業務流程的切入點和主線

Interaction: 主要體現在對role的建模,role是每個context中複雜的業務邏輯的真正執行者。role所做的是對行為進行建模,它連線了context和領域物件。由於系統的行為是複雜且多變的,role使得系統將穩定的領域模型層和多變的系統行為層進行了分離,由role專注於對系統行為進行建模。

DCI目前廣泛被看作是對DDD的一種發展和補充,用在基於面向物件的領域建模上。顯式的對role進行建模,解決了面向物件建模中的充血模型和貧血模型之爭。DCI透過顯式的用role對行為進行建模,同時讓role在context中可以和對應的領域物件進行繫結(cast),從而既解決了資料邊界和行為邊界不一致的問題,也解決了領域物件中資料和行為高內聚低耦合的問題。跟DDD將物件和行為靜態結合不同,DCI結合上下文將role代表的行為和物件動態結合。

DDD與六邊形架構

依賴倒置原則由Robert C。 Martin提出,正式定義為:

高層模組不應該依賴於底層模組,兩者都應該依賴於抽象。抽象不應該依賴於細節,細節應該依賴於抽象。

根據該定義,DDD分層架構中的低層元件應該依賴於高層元件提供的介面,即無論高層還是低層都依賴於抽象,我們將原來位於底層的基礎設施層調整到最高層,然後依賴下面各層提供的抽象介面,那麼就解決了之前基礎設施層反向依賴領域層的問題,同時原來的領域層位於最底層,不再包含持久化等,關於可測試性問題也迎刃而解,調整後的層次結構如上圖B所示。

上面只是採用依賴倒置原則實現的一種架構表現,因為依賴倒置原則要求所有層次都依賴抽象,這就意味著層次可以“平等相處”,已經失去存在的必要,這時的層次架構如同在依賴倒置原則的“重力效應”下瞬間坍縮一樣,形成下圖所示的一種具有對稱性特徵的架構風格,即六邊形架構:

領域驅動設計(DDD)理論與方法

六邊形架構也叫埠和介面卡架構,是Alistair Cockburn於2005年在其部落格中提出。他對該架構的一句話定義是:應用應能平等地被使用者、其他程式、自動化測試或指令碼驅動,也可以獨立於其最終的執行時裝置和資料庫進行開發和測試。該架構由埠和介面卡組成,所謂埠是應用的入口和出口,在許多語言中,它以介面的形式存在。例如以取消訂單為例,“傳送訂單取消通知”可以被認為是一個出口埠,訂單取消的業務邏輯決定了何時呼叫該埠,訂單資訊決定了埠的輸入,而埠為預訂流程遮蔽了通知傳送方式的實現細節。

而介面卡分為兩種,主介面卡(別名Driving Adapter)代表使用者如何使用應用,從技術上來說,它們接收使用者輸入,呼叫埠並返回輸出。Rest API是目前最常見的應用使用方式,以取消訂單為例,該介面卡實現Rest API的Endpoint,並呼叫入口埠CancelOrderService。同一個埠可能被多種介面卡呼叫,例如CancelOrderService也可能會被實現訊息協議的Driving Adapter呼叫以便非同步取消訂單。

次介面卡(別名Driven Adapter)實現應用的出口埠,向外部工具執行操作,例如

向MySQL執行SQL,儲存訂單

使用Elasticsearch的API搜尋產品

使用郵件/簡訊傳送訂單取消通知

若將其視覺化,Driving Adapter和Driven Adapter基於埠圍繞著應用和域形成左右結構:

領域驅動設計(DDD)理論與方法

其中值得注意的是,中間核心區的業務邏輯是由Application和Domain組成,即由分層架構中的應用層和領域層合併而來。圖例中的Controller、Listener和Template是北向介面卡(Driving Adapter),JPARepository、EventPublisher、DomainService是南向介面卡(Driven Adapter),業務邏輯核心區中的不同Application Service組成不同的埠。如果將六邊形架構和傳統的分層架構做個對比,那就是:

領域驅動設計(DDD)理論與方法

領域驅動設計要素

在DDD中,軟體的核心是其為客戶解決領域相關的問題的能力。這裡的領域,就是指軟體系統要解決的實際問題相關的東西的集合。例如:為一個電子商務公司開發一個電商系統,我們就需要圍繞這個盈利模式的運營方式、業務規則,比如如何進貨,如何促銷,如何物流等等了解這個電子商務公司的盈利模式,所有和業務相關的東西都屬於領域。領域分為問題域和解決方案域兩部分。

為了分解問題域的複雜度,問題域又會被拆解為多個子域,每個子域都要明確待解決的業務問題和業務流程,以及透過解決業務問題為企業帶來了什麼樣的業務價值(這個是因,業務流程和要解決的業務問題是果)。

在清晰的定義子域後,我們就可以建立通用語言來提取該子域的領域知識,並基於通用語言為解決問題建立領域模型。領域模型是關於某個特定業務領域的軟體模型。通常,領域模型透過物件模型來實現,這些物件同時包含了資料和行為,並且表達了準確的業務含義。

一個領域模型會存在於一個限界上下文中。限界上下文在DDD中用來定義模型的適用範圍、模型的用途、以及在何處保持一致,限界上下文會讓團隊明確模型的職責邊界是什麼。同時,通用語言被限定在限界上下文中;限界上下文提供了一個語義邊界,在每個限界上下文內通用語言的每個詞彙必須和領域概念一一對應。理想條件下,子域和限界上下文是一一對應。但是子域劃分的粒度,遺留系統的現狀,語言的歧義,團隊結構等子域和限界上下文對應可能是1:N或者N:N的。透過限界上下文間的對映,上下文中的多個模型會協作以滿足系統需求。我們也可以瞭解在不同上下文中的同名詞彙是否存在關係,存在什麼樣的關係。對通用語言而言,子域解釋了通用語言和現實世界業務活動的關係;限界上下文提供了一個語義邊界,來保持通用語言和領域概念的一一對應關係;上下文對映則提供了不同限界上下中的通用語言的轉換關係。

DDD領域驅動設計通常會包含戰略設計和戰術設計兩部分:

戰略設計:重業務建模,以業務視角,拆分領域,透過事件風暴(從發散到收斂過程),梳理業務並構建領域模型,這塊過程會涉及業務人員、產品人員、架構師等多方參與

戰術設計:重落地實現,以構建的領域模型,建立了領域模型的邊界與上下文,也就確認了微服務的邊界,這個過程會涉及架構師、技術人員參與

下面的圖展示了DDD設計開發的一般步驟和涉及到的戰略設計和戰術設計相關的概念與要素:

領域驅動設計(DDD)理論與方法

領域驅動設計(DDD)理論與方法

領域驅動設計(DDD)理論與方法

原型與4色原型建模法

在接手一個新的業務需求,首先要明確業務邊界和要解決的問題,然後透過一種方法將業務活動涉及到的各種要素進行分解,抽象出原型,然後描述各原型之間的協作方式,透過UML類圖的形式視覺化呈現出來。最後跟業務人員和領域專家一起協作完善,保證最後開發的系統能體現業務方的需求。

原型一詞來自於希臘語archetypo (arcetupo), 意味著 “original pattern。” (原始模式)。 比如英雄這個原型在美國或在中國看上去可能不一樣,但是英雄就是英雄,我們還是能夠很快地總結出英雄的一些特點。 因為原型是人類組織、總結和概括客觀世界的基本概念,我們完全有理由在軟體開發領域應用這些概念。

OO軟體系統是對業務領域(business domain)的思考抽象並進行管理操作,注意業務領域這個概念,我們相信應該能在業務領域中發現原型,或者在軟體系統;或者這些系統模型中。我們稱這種原型型別為業務原型(business archetype)

一個業務原型應該是一個在業務領域或商業軟體系統持續發生並且普遍存在的最初級的事物。參與方Party是一個業務原型例子,一個Party代表可標識的、可定位的單元或單位,這些單位有正常的狀態。通常一個Party用來表示某個人或某個組織。所有商業系統都或多或少有Party概念。

原型之間是相互互動的,Party, Product和Order是每個虛擬商業系統的基本概念,在這個商業系統中,你可以賣產品或服務。我們將這些原型之間的協作看成是業務原型模式(business archetype patterns):它是在業務領域或商業軟體系統持續發生並且普遍存在業務原型之間的協作。

四色原型是誕生於90年代,被廣泛使用的一種系統分析方法,在域建模階段,將4種不同的業務原型按照不同的顏色標出。這4種原型分別為:

時刻-時段原型(Moment-Interval Archetype,簡稱MI):一段時間內發生的業務,包括與業務相關的資料以及行為。資料及行為是可以追蹤的,如賣東西是在某個時刻發生的,它有發生日期和時間等相關記錄資訊。時刻-時段原型使用粉紅顏色表示。

角色原型(Role Archetype):任何一個系統都需要人或某個組織介入執行,例如論壇系統需要註冊者角色發言;銷售訂單需要業務員角色制定等。角色模型使用黃色標識。

參與方-地點-物品原型(Part-Place-Thing Archetype,簡稱PPT):表示參與扮演不同角色的參與方或地點或事物。Party表示有自己正常的狀態並且能夠自主控制自己的一些行為,如人或組織, place, or thing表示沒有自主行為,如某個地方或某個事情。Part-Place-Thing都可以成為角色,也就是透過扮演角色參與活動。如商品可以是售賣中的商品也可以是使用中的商品,可以在不同場景扮演不同的角色。參與方-地點-物品原型使用綠色表示。

描述原型(Description Archetype,簡稱DESC):Desc是對PPT的抽象概念,它是PPT的特性的總結,每一個PPT都屬於一個(種)Desc。比如每一臺出廠的車輛都屬於車輛種類的一輛,每一個具體的人都屬於人種的一員。PPT原型是對單個個體的抽象,比如一輛具體的轎車可以用車輛編號、車輛顏色、車輛型號等描述,一個人可以用身份證號碼、身高、體重、喜好來描述。而對車輛種類的抽象描述為生產廠家、生產批號、適用顏色等等,當然轎車種類通常還可以劃分為微型轎車、普通轎車、高階轎車等。同理對於人種包含歐羅巴人種(又稱白色人種或高加索人種或歐亞人種)、蒙古人種(又稱黃色人種或亞美人種)、尼格羅人種(又稱黑色人種或赤道人種),對於人種的描述可以是膚色、地獄分佈、主要使用語言等。描述原型用藍色表示。

4色原型是對物理世界的建模方式,如果以PPT為中心,那麼以上概念可以總結為:什麼東西(人或事物)在什麼地方透過什麼方式(角色)在什麼時刻進行操作,類似於5W2H分析法。4色原型的一個直接好處是幫助我們快速地梳理不同的業務原型,透過類圖的方式繪畫不同原型及其之間的關係,在實際建模過程中可以採取以下步驟分辨不同原型:

第一:它是不是依賴時間長瞬間或一段短時間存在的,是不是業務需求需要跟蹤記錄的物件?如果是,它就是MI原型。

第二:它是不是角色呢?如果是,就屬於黃色Role原型。

第三:它是不是屬於一種種類性質物件,或者代表一組可以反覆使用的概念,如果是,它就是藍色Description原型。

第四:它是某人或組織?或者是某個地方或者某個東西?那它就是綠色的PPT原型。

下文透過實際案例結合5W2H分析法和四色原型法來描述DDD建模過程。

DDD建模案例

燃氣抄表計費場景每月末,燃氣公司制定抄表計劃並批次生成抄表任務,抄表任務透過工單的形式下發到抄表人員到客戶現場抄表,抄表完成之後給客戶應收賬單,客戶可以現場繳費或者延後透過線上自助繳費。下面以此案例描述建模步驟。

1 描述業務場景

用5W2H進行分析:使用者(WHO)在什麼環境(WHERE)下遇到什麼時機(WHEN)因為什麼(WHY)產生什麼目標(WHAT),繼而透過什麼方法(HOW)去達成目標。大部分場景不需要考慮HOW MUCH。

領域驅動設計(DDD)理論與方法

透過本案例的分析,可以得到以下需求場景清單:

領域驅動設計(DDD)理論與方法

2 梳理業務流程

在得到場景清單後,我們把各個場景串聯起來用業務流程的方式表達出來:

領域驅動設計(DDD)理論與方法

3 尋找時標性物件

時標性物件對應到時刻-時段原型,是我們關心的可追溯的業務事件。透過將業務流程分解到業務過程的最小粒度來尋找,然後繪製以下格式的表格:

領域驅動設計(DDD)理論與方法

接著將這些時標性物件(用紅色標記)的關係描述出來,就得到了領域模型的骨幹:

領域驅動設計(DDD)理論與方法

4 尋找時標物件周圍的“人、地、物”

在“時標”物件周圍的用綠色所表示的“人、地、物”概念,如下圖所示:

領域驅動設計(DDD)理論與方法

5 抽象“角色”

在上圖中插入用黃色所表示的“角色”概念,如下圖所示:

領域驅動設計(DDD)理論與方法

6 補充“描述”資訊

在上圖中插入用藍色所表示的“描述”概念,描述物件包括“人、地、物”和時標物件,如下圖所示:

領域驅動設計(DDD)理論與方法

7 劃分限界上下文

在本案例中,抄表繳費屬於核心業務,將業務過程中的時標物件都劃分到核心子域,但核心子域解決的問題有所不同,比如抄表計劃和抄表任務指派屬於抄表前期規劃階段,參與人員同其他階段都不同,因此將其劃分到計劃上下文。同樣,抄表記錄屬於抄表的實施階段,劃分到抄表上下文,繳費屬於抄表後期階段,劃分到繳費上下文,如下圖:

領域驅動設計(DDD)理論與方法

8 確定聚合及聚合根

在每個上下文中,對時標物件中的概念按照相近原則劃分為不同的組,形成新的聚合,然後選出聚合根作為與外界互動的代表。比如抄表時標物件產出的核心資料是待繳費的應收賬單,關聯物件涉及到抄表員、客戶和表具,而其他暫時無法分組的概念或屬性獨立為值物件,待需求需要的時候進一步處理。淺綠色代表聚合根,下圖所示:

領域驅動設計(DDD)理論與方法

同理,繼續尋找其他上下文中的聚合和聚合根,如繳費和支付:

領域驅動設計(DDD)理論與方法

注意上圖中支付和繳費、抄表都涉及到賬單聚合根,但是因為賬單關注的點有所區別,比如抄表關心的是應收賬單,繳費關心的是實收賬單,支付賬單關心的是無業務含義的交易流水和交易賬號,不同關注點透過限界上下文隔離。

9 最佳化調整子域劃分

透過聚合的過程我們發現,客戶和表具在多個上下文中共用,是可以將其獨立出來,作為共享上下文,劃分到通用子域中。另外,在繳費環節,因為支付通常作為基礎功能,支撐著繳費等支付相關業務,因此也劃分到獨立的支撐子域,調整後的子域限界上下文如圖,其中虛線橢圓標識的客戶、表具屬於共享子域:

領域驅動設計(DDD)理論與方法

作者:聞乃松

原文連結:https://mp。weixin。qq。com/s?__biz=MzI3ODU3MzQ2Ng==&mid=2247484331&idx=1&sn=7da3e9a6f2e7acd5dd60eae574575dc9

如果覺得本文對你有幫助,可以轉發關注支援一下