DDD領域驅動設計實戰-聚合(Aggregate)和聚合根(AggregateRoot)

      網友投稿 1669 2022-05-30

      實體和值對象組成聚合,再根據業務,將多個聚合劃定到同一限界上下文,并在限界上下文內完成領域建模。

      聚合只是單純將一些共享父類、密切關聯的對象聚集成一個對象樹嗎?如果是這樣,對于存在于這個樹中的對象,有沒有一個實用的數目限制?

      既然一個聚合可以引用另一個聚合,是否可以深度遍歷下去,并且在此過程中修改對象?

      聚合的不變條件和一致性邊界是什么意思?

      1 聚合

      實體一般對應業務對象,具有業務屬性和業務行為

      值對象主要是屬性集合,描述實體的狀態和特征

      但都只是個體化對象,其行為表現出的是個體能力。

      1.1 意義

      領域模型內的實體和值對象好比個體,而能讓實體和值對象協同工作的組織就是聚合,用來確保這些領域對象在實現共同的業務邏輯時,能保證數據的一致性。

      聚合由業務和邏輯緊密關聯的實體和值對象組合而成,是數據修改和持久化的基本單元,每個聚合對應一個倉儲,實現數據的持久化。

      聚合有一個聚合根和上下文邊界:

      該邊界根據業務單一職責和高內聚原則,定義了聚合內部應該包含哪些實體和值對象

      聚合之間的邊界是松耦合的

      按這種方式設計出來的微服務自然就是高內聚、低耦合。

      聚合屬于領域層,領域層包含多個聚合,共同實現核心業務邏輯。聚合內的實體以充血模型實現個體業務能力,以及業務邏輯的高內聚。

      跨多個實體的業務邏輯通過領域服務來實現,跨多個聚合的業務邏輯通過應用服務來實現:

      有的業務需同一聚合的A和B兩個實體共同完成,就可將這段業務邏輯用領域服務實現

      有的業務需聚合C和聚合D中的兩個服務共同完成,使用應用服務來組合這倆服務

      2 聚合根

      為避免由于復雜數據模型缺少統一的業務規則控制,而導致聚合、實體之間數據不一致。

      傳統數據模型中的每一個實體都是同級對等,若任由實體無管控地調用數據修改,可能導致實體之間數據邏輯的不一致。而若使用鎖則會增加代碼復雜度,降低系統性能。

      若把聚合比作組織,則聚合根就是該組織負責人。聚合根也稱為根實體,它不僅是實體,還是聚合的管理者。

      作為實體,擁有實體的屬性和業務行為,實現自身的業務邏輯

      作為聚合的管理者,在聚合內部負責協調實體和值對象按照固定業務規則協同完成共同的業務邏輯

      在聚合間,它還是聚合對外的接口人,以聚合根ID關聯的方式接受外部任務和請求,在上下文內實現聚合之間的業務協同。即聚合間通過聚合根ID關聯引用,若需要訪問其它聚合的實體,就要先訪問聚合根,再導航到聚合內部實體,外部對象不能直接訪問聚合內實體。

      2.1 電商案例

      典型的聚合根:庫存、商品、訂單等。

      以訂單為例,訂單在聚合里是聚合根,與訂單關聯的有訂單明細和收貨地址:

      訂單明細包括商品ID、商品名稱、價格及數量等。由于訂單明細是多個,它是一個集合,它被設計為實體,被訂單引用

      訂單只有一個收貨地址,收貨地址的值源于你的個人中心維護的收貨地址,收貨地址只能被整體替換,所以設計為值對象

      3 聚合設計案例

      DDD領域建模通常采用事件風暴,采用用例分析、場景分析和用戶旅程分析等方法,通過頭腦風暴列出所有可能的業務行為和事件,然后找出產生這些行為的領域對象,并梳理領域對象之間的關系,找出聚合根,找出與聚合根業務緊密關聯的實體和值對象,再將聚合根、實體和值對象組合,構建聚合。

      如下為保險的投保業務場景:

      采用事件風暴,根據業務行為,梳理出在投保過程中發生這些行為的所有的實體和值對象

      從眾多實體中選出適合作為對象管理者的根實體(聚合根)。判斷一個實體是否是聚合根,可分析:是否有獨立生命周期?是否有全局唯一ID?是否可創建或修改其它對象?是否有專門模塊管這個實體

      根據業務單一職責和高內聚原則,找出與聚合根關聯的所有緊密依賴的實體和值對象。構建出 1 個包含聚合根(唯一)、多個實體和值對象的對象集合,這個集合就是聚合

      在聚合內根據聚合根、實體和值對象的依賴關系,畫出對象的引用和依賴模型。

      投保人和被保人的數據,是通過關聯客戶ID從客戶聚合中獲取的,在投保聚合里它們是投保單的值對象,這些值對象的數據是客戶的冗余數據,即使未來客戶聚合的數據發生了變更,也不會影響投保單的值對象數據。

      從圖還可看出實體之間的引用關系,比如在投保聚合里投保單聚合根引用了報價單實體,報價單實體則引用了報價規則子實體。

      多個聚合根據業務語義和上下文一起劃分到同一個限界上下文內。

      4 設計原則

      4.1 在一致性邊界內建模真正的不變條件

      DDD領域驅動設計實戰-聚合(Aggregate)和聚合根(AggregateRoot)

      要從限界上下文中發現聚合,需要了解模型中真正的不變條件,才能決定什么樣的對象可以放在一個聚合。不變條件表示一個業務規則,該規則應該總是保持一致。

      有多種類型的一致性:

      事務一致性

      要求立即性和原子性

      最終一致性

      在討論不變條件時,我們討論的是事務一致性。我們可能有以下不變條件:

      c = a + b

      當a等于2,b等于3時,c必等于5。根據這條規則,如果c不為5,便違背系統不變條件。為保持c的一致性,應該在模型中為這些屬性設計一個邊界:

      AggregateTypel ( int a; int b; int c; operations ...

      聚合邊界之內的所有內容組成一套不變的業務規則,任何操作都不能違背這些規則。邊界之外的任何東西與該聚合都無關。因此,聚合表達了與事務一致性邊界相同的意思。該例中,AggregateTypel擁有3個int屬性,任何聚合都可擁有不同類型的屬性。

      聚合用來封裝真正的不變性,而非簡單地組合對象。聚合內有一套不變的業務規則,各實體和值對象按統一業務規則運行以實現對象數據的一致性,邊界之外的任何東西都與該聚合無關,所以聚合能實現高內聚!

      4.2 優先小聚合

      聚合設計過大,會因為包含過多實體,導致實體間管理復雜,高頻操作時出現并發沖突或數據庫鎖,即便能保證事務成功執行,依然有可能限制系統的性能和可伸縮性。

      小聚合則可降低由于業務過大導致聚合重構的可能性,讓領域模型更能適應業務變化。

      最極端的,一個聚合只有全局標識和單個屬性,當然,這并非推薦的做法(除非正是需求所在)。推薦使用根實體(Root Entity)表示聚合,其中只包含最小數量的屬性或值類型屬性。

      最簡單的:必須與其他屬性保持一致的。比如,一個Product擁有name、description屬性,它們需要保持一致,將它們放在兩個不同的聚合中根本無意義。修改name時,很可能也會同時修改description。若只修改其一,多半是在修改語法上的錯誤或使description能更匹配name。

      該部分是否:

      會隨時間而改變

      或能被全部替換

      若可被全部替換,請將其建模成值對象,而非實體

      很多時候,建模成實體的概念都可重構成值對象。優先選用值對象并非意味著聚合就是不變的,因為當值對象屬性被替換成其他值時,根實體也就隨之改變。

      將聚合的內部建模成值對象有很多好處:

      據所選用持久化機制,值對象可隨根實體而序列化,而實體則需單獨的存儲區域并予以跟蹤

      實體還會帶來一些不必要操作,如在使用Hibernate時,需對多表聯合查詢,而對單表讀取快得多,而使用值對象也更方便安全

      由于值對象不變,測試也相對簡單

      小聚合不僅有性能和可伸縮性上的好處,它還有助于事務成功執行,即可減少事務提交沖突。系統可用性也得到增強。

      在你的領域中,迫使你設計大聚合的不變條件約束并不多。遇到這種情況,可考慮添加實體或集合,但始終都應將聚合設計得盡量小。

      4.3 通過唯一標識引用其它聚合

      聚合之間通過關聯外部聚合根ID的方式引用,而非直接對象引用。外部聚合的對象放在聚合邊界內管理,容易導致聚合的邊界不清晰,也會增加聚合之間的耦合度。

      4.4 在邊界之外使用最終一致性

      聚合內數據強一致性,聚合間數據最終一致性。

      一次事務中,最多只能更改一個聚合的狀態。若一次業務操作涉及多個聚合狀態的更改,應采用領域事件異步修改相關的聚合,實現聚合間的解耦。

      在不持有對象引用的情況下,不能修改其他聚合,因此可避免在同一事務中修改多個聚合。但這樣限制性太強,因為在領域模型中,我們總需要對象之間的關聯關系來完成任務。對此,又該怎么辦呢?

      4.5 通過應用層實現跨聚合的服務調用

      為實現微服務內,聚合之間的解耦,還有未來以聚合為單位的微服務的組合和拆分,應避免跨聚合的領域服務調用和跨聚合的數據庫表關聯。

      5 總結

      聚合的特點

      高內聚、低耦合,它是領域模型中最底層的邊界,可作為拆分微服務的最小單位,但不推薦過度拆分。在對性能有極致要求的場景中,聚合可獨立作為一個微服務,以滿足版本的高頻發布和彈性伸縮要求。

      一個微服務可包含多個聚合,聚合之間的邊界是微服務內天然的邏輯邊界。有了該邏輯邊界,在微服務架構演進時就可以聚合為單位進行拆分和組合。

      聚合根的特點

      聚合根是實體,有實體的特點,具有全局唯一標識,有獨立的生命周期。一個聚合只有一個聚合根,聚合根在聚合內對實體和值對象采用直接對象引用的方式進行組織和協調,聚合根與聚合根之間通過ID關聯的方式實現聚合之間的協同。

      實體的特點

      有ID標識,通過ID判斷相等性,ID在聚合內唯一即可。狀態可變,它依附于聚合根,其生命周期由聚合根管理。實體一般會持久化,但與數據庫持久化對象不一定是一對一的關系。實體可引用聚合內的聚合根、實體和值對象。

      值對象的特點

      無ID,不可變,無生命周期,用完即扔。值對象之間通過屬性值判斷相等性。它的核心本質是值,是一組概念完整的屬性組成的集合,用于描述實體的狀態和特征。值對象盡量只引用值對象。

      參考

      《實現領域驅動設計》

      聚合和聚合根:怎樣設計聚合?

      微服務

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Android四大組件之Service
      下一篇:關于 ABAP Netweaver 的一些基礎知識
      相關文章
      精品久久久久久亚洲中文字幕| 亚洲AV成人一区二区三区AV| 亚洲av无码专区青青草原| 亚洲男人的天堂在线播放| 亚洲一区日韩高清中文字幕亚洲| 色综合久久精品亚洲国产| 一本色道久久88—综合亚洲精品 | 亚洲毛片网址在线观看中文字幕| MM1313亚洲精品无码久久| MM1313亚洲精品无码久久| 亚洲大尺度无码无码专线一区| 亚洲熟妇无码一区二区三区| 亚洲一卡一卡二新区无人区| 亚洲色精品VR一区区三区| 亚洲一级毛片免费在线观看| 亚洲一区二区三区乱码在线欧洲| 亚洲乱码在线观看| 亚洲精品成a人在线观看夫| 国产精品亚洲一区二区三区在线观看| 自拍偷自拍亚洲精品播放| 国产精品亚洲av色欲三区| 亚洲国产精品一区二区第一页免 | 亚洲成AV人网址| 国产亚洲精品免费视频播放| 亚洲人成网亚洲欧洲无码久久| 亚洲国产成人一区二区三区| 亚洲另类激情综合偷自拍| 亚洲成a人片在线观看中文!!!| 亚洲午夜精品一区二区公牛电影院| 亚洲三级在线播放| 亚洲精品9999久久久久无码| 国产成人亚洲精品无码AV大片| 亚洲精品456播放| 久久精品国产69国产精品亚洲| 亚洲午夜视频在线观看| 亚洲人成影院在线高清| 亚洲暴爽av人人爽日日碰| 亚洲国产午夜福利在线播放 | 亚洲女久久久噜噜噜熟女| 亚洲日韩区在线电影| 亚洲一区二区三区播放在线|