DDD理論學習系列(10)-- 聚合

来源:http://www.cnblogs.com/sheng-jie/archive/2017/07/13/7158555.html
-Advertisement-
Play Games

"DDD理論學習系列——案例及目錄" 1.引言 聚合,最初是UML類圖中的概念,表示一種強的關聯關係,是一種整體與部分的關係,且部分能夠離開整體而獨立存在,如車和輪胎。 在DDD中,聚合也可以用來表示整體與部分的關係,但不再強調部分與整體的獨立性。聚合是將相關聯的領域對象進行顯示分組,來表達整體的概 ...


DDD理論學習系列——案例及目錄


1.引言

聚合,最初是UML類圖中的概念,表示一種強的關聯關係,是一種整體與部分的關係,且部分能夠離開整體而獨立存在,如車和輪胎。

在DDD中,聚合也可以用來表示整體與部分的關係,但不再強調部分與整體的獨立性。聚合是將相關聯的領域對象進行顯示分組,來表達整體的概念(也可以是單一的領域對象)。比如將表示訂單與訂單項的領域對象進行組合,來表達領域中訂單這個整體概念。

我們知道,領域模型是由一系列反映問題域概念的領域對象(實體和值對像)組成,聚合正是應用在領域對象之上。如果要正確應用聚合,我們首先得理清領域對象間的關聯關係。

2. 梳理關聯關係

在設計領域模型的初期,我們習慣專註於領域中的實體和值對象,而忽略領域對象之間的關聯關係,以至於我們會基於現實業務場景或數據模型來建立關聯關係。這樣就會引入大量不必要的關聯,比如下圖:

存在大量關聯關係的複雜領域模型

然而圖中的關聯關係都是必要的嗎?我想未必。這樣的關聯關係,加大了實現領域模型的技術難度。

當我們建立對象的關聯關係時,思考以下問題:

  1. 這個關聯關係的作用時什麼?
  2. 誰需要這個關聯關係去發揮作用?

而如何簡化關聯呢?

  1. 基於業務用例而非現實生活建立必要的關聯
  2. 減少不必要的關聯
  3. 將雙向的關聯轉換為單向關聯

如果遵從這個原則,那我們的領域模型將會是這樣的:

基於必要的關聯關係的領域模型

領域對象間清晰的關聯關係,能夠清晰反映領域概念,便於我們設計出比較理想的領域模型。理清了領域對象間的關聯關係,我們下麵來應用聚合。

3. 應用聚合

領域對象不是孤立存在的,往往幾個對象的組合才能表示一個完整的概念,如上文所說的訂單和訂單項。那如何組合對象呢?也就是我們本文的主題。
聚合是領域對象的顯示分組,旨在支持領域模型的行為和不變性,同時充當一致性和事務性邊界。
這句話涉及到幾個概念,我們來拆解一下:

  1. 領域對象的顯示分組
  2. 領域行為和不變性
  3. 一致性和事務性邊界

其中我們需要澄清下領域不變性

Domain invariants are statements or rules that must always be adhered to.
領域不變性指的是必須遵守的陳述或規則。換句話說,就是領域內我們關註的業務規則。比如,訂單必須具有唯一訂單編號、訂單日期;訂單必須冗餘商品的基本信息(名稱、價格、折扣);訂單至少有一個商品,刪除商品時,訂單項需要一併刪除;等等。

前兩句話綜合來說,就是聚合通過對領域對象的封裝來體現領域中的業務規則。
而邊界的目的是分離聚合內外,聚合內通過事物來保證強一致性。

總而言之,聚合不僅僅是簡單的對象組合,其主要的目的是用來封裝業務和保證聚合內領域對象的數據一致性。

一致性和事務性邊界,又如何理解呢?
一致性是指數據一致性,事務性指的資料庫的ACID原則。
下麵我們來著重介紹下。

4.一致性邊界

為了確保系統的可用性和可靠性,我們必須保證數據的一致性。

訂單支付成功後,訂單狀態要更新為已支付狀態,且現有庫存要根據訂單中商品實際銷售數量進行扣減。

下麵我們就以這個案例,來分析說明。

4.1.事務一致性

針對這個用例,傳統的做法就是,在一個事務中,去更新訂單狀態和扣減庫存。這樣似乎滿足了業務場景需求,但是我們不得不考慮另外一個問題——併發衝突。比如,在更新訂單的同時,商城來了一批貨,要進行庫存更新,這個時候就存在潛在的衝突,而問題可能表現為資料庫級別的阻塞或更新失敗(由於悲觀併發),如下圖:

大的事務邊界導致併發衝突

這個併發問題我們該如何解決呢?
首先我們要分析問題的原因,這個用例陳述了具體的業務規則。我們錯誤的將業務涉及到的所有領域對象都放到了一個事務性邊界中去了。其實這個用例涉及到三個子域,銷售、商品、庫存子域。從領域不變性的角度來看,我們應該維護各自子域內業務規則的不變性,而不是為了業務場景實現一概而論。按照這個思想,我們把訂單、商品、庫存拆分成三個獨立的聚合,如下圖所示。

根據領域不變性設置事務邊界

從圖中我們可以看出,每個聚合都有自己的事務一致性邊界。也就是說這三個聚合分別在不同的事務中維持自己的不變性,也就是說聚合是用來維護內部事務一致性。那針對以上用例,明顯需要跨域多個聚合,我們又該如何保證一致性呢?因為我們不能在一個事務中更新多個聚合,所以我們只能實現最終一致性。

4.2. 最終一致性

最終一致性的實現原理是藉助領域事件來完成事務的拆分,如下圖所示。

通過領域事件實現最終一致性

而針對我們的用例,在更新訂單支付狀態時,發佈一個訂單已支付的領域事件,庫存聚合訂閱處理這個事件,即可完成庫存的更新。事務拆分如下圖:

庫存聚合最終一致性

4.3. 特殊情況

凡事沒有絕對,在一個聚合中僅修改一個聚合是最佳方法。但有時候,在一個事務中更新多個聚合也是可行的,這需要結合具體場景區別對待。另外還有一點需要澄清,以上使用一致性的目的,主要是針對聚合的修改。在一個事務中載入和創建多個聚合是沒有問題的,因為並不會導致併發衝突。

5. 聚合的設計

根據上面的闡述:聚合不僅僅是簡單的對象組合,其主要的目的是用來封裝業務和保證聚合內領域對象的數據一致性。

那聚合設計時要遵循怎樣的原則呢?

  1. 遵循領域不變性
  2. 聚合內實現事務一致性,聚合外實現最終一致性
    一個事物一次僅更新一個聚合。當業務用例要跨域多個聚合時,使用領域事件進行事務拆分,實現最終一致性。
  3. 基於業務用例而非現實生活場景
  4. 避免成為集合或容器
    對聚合的一大誤解就是,把聚合當作領域對象的集合或容器。當發現這個徵兆時,你要考慮你聚合是否需要改造。
  5. 不僅僅是HAS-A關係
    聚合不是簡單的包含關係,要確定包含的領域對象是否為了滿足某個行為或不變性。
  6. 不要基於用戶界面設計聚合
    聚合不應該根據UI界面的需求進行設計。而應該通過載入多個聚合數據映射到UI展示需要的視圖模型中。
  7. 創建具有唯一標識的聚合根
    聚合根作為聚合的網關,通過聚合根完成聚合中領域對象的持久化和檢索。
  8. 優先使用值對象
    聚合根內的其他領域對象優先設計成值對象
  9. 使用ID關聯,而非對象引用
    對象引用不僅會導致聚合邊界的模糊,而且會導致延遲載入的問題。
  10. 通過唯一標識引用其他聚合
    聚合邊界之外的對象不能持有聚合內部對象的引用;聚合內部的領域對象可以持有其他聚合根的引用。
  11. 避免在聚合內使用依賴註入
    對於依賴的對象,我們應該在調用聚合方法之前查找獲取並通過參數傳遞。可以在應用服務中通過依賴註入資源庫或領域服務獲取聚合依賴的對象,然後傳入聚合。
  12. 使用小聚合
    通常,較小的聚合使系統更快且更可靠,因為更少的數據傳輸以及更少的併發衝突。
    大聚合會影響性能:聚合的每一個成員都增加了從資料庫載入和保存到資料庫的數據量,直接影響到性能。
    大聚合容易導致併發衝突:大的聚合可能有多個職責,意味著它涉及到多個業務用例。我們可以量化一個聚合涉及到的業務用例數,數量越大,設計的聚合邊界越應該被質疑,嘗試將其細化拆解成小聚合。
    大聚合擴展性差:聚合的設計要關註可擴展性。大聚合可能會跨越多個資料庫表或文檔,這就在資料庫級別形成了耦合,它將阻礙你對數據子集進行數據遷移。同時,在業務改變時,大聚合不能很好的適應變化。

6.最後

聚合是一個複雜的概念,其正確應用的關鍵是領域對象間關聯關係的把握和領域不變性的理解。其實現的難點在於一致性的維護上:聚合內實現事務一致性,聚合外實現最終一致性。聚合的設計是一個持續性的活動,不可能在初始階段就能設計出完美的聚合,我們應該根據對領域知識的深入和經驗的積累持續改進聚合的設計。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天解決了第11章_不顯示博文表單的問題,時間太晚了,先留個坑,明天寫出詳細的解決方案。 同時也將前11章(當我結束本書學習時會將所有的問題一一列出並給出中文的解決方案)的常見問題給予解答。 ...
  • basestring basestring() 說明:basestring是str和unicode的超類(父類),也是抽象類, 因此不能被調用和實例化,但可以被用來判斷一個對象是否為str或者unicode的實例, isinstance(obj, basestring) 等價於isinstance( ...
  • 什麼題目都不會做於是開始做搜索題。 然而我搜索題也不會做了。 鐵定沒戲的蒟蒻。 1.NOIP2004 蟲食算 “對於給定的N進位加法算式,求出N個不同的字母分別代表的數字,使得該加法算式成立。輸入數據保證有且僅有一組解”。 大概就是給你一堆(n個)字母讓你求出n進位下的一個n位數加n位數得到n位數的 ...
  • Java作為一面向對象的語言,具備面向對象的三大特征——繼承,多態,封裝。 繼承顧名思義,繼任,承接,傳承的意思。面向對象的語言有一個好處,就是可以用生活中的例子來說明面向對象的特性。那麼我們先來看看生活中的繼承關係有哪些?最常見的:父母子女;汽車,電動車,自行車和車。無論哪種車,都有具備車的特性。 ...
  • 1 result標簽是乾什麼的 就是結果,伺服器處理完返回給瀏覽器的結果;是一個輸出結果數據的組件 2 什麼時候需要指定result標簽的類型 把要輸出的結果數據按照我們指定的數據類型進行處理 3 常見的類型(在struts的預設配置文件120行有這些類型的列表) 3.1 dispatcher 轉發 ...
  • 堡壘機的表結構圖: ...
  • 測試 ...
  • 頁面效果 1.當新增用戶時,保存用戶信息,並刷新用戶管理首頁 2.當編輯已存在的用戶時,更新用戶信息,並刷新用戶管理首頁 實現步驟 1.Action類中定義save()及edit() 2.struts.xml中添加 用戶管理<action>下添加 3.service實現類中定義用戶保存方法 4.在w ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...