DDD學習筆記五

模型引力場:聚合

  • 強作用力體現:
    某個領域模型是另一些模型存在的前提,沒有前者,后者就失去了生存的意義。
    一組領域模型之間存在關聯的領域邏輯,任何時候都不能違反。
    一組領域模型必須以一個完整的、一致的狀態呈現給外部,而不能是某個中間彼此不和諧的狀態。

  • 當模型之間的相互作用力強到一定程度的時候,將產生質變,必須把它們當作一個整體,我們稱之為聚合

  • 聚合是一組相關對象的集合,可以稱其為數據修改單元或者事務一致性組合單元。
    每個聚合都有一個根(root)和一個邊界(boundary)。
    邊界定義了聚合內部包含的內容,
    根則是聚合所包含的一個特定的實體(entity)。

  • 聚合概念的必要性:
    1)保證模型存在的意義。聚合內的成員脫離聚合根的存在是沒有意義的,比如支付之于訂單。這不是一個技術問題,而是一個領域邏輯問題,領域專家和用戶不會在意一個沒有訂單的支付對象。
    2)時刻保持領域內在邏輯不被違反。這里的關鍵字是“時刻”,即不允許在任何時候把不和諧的狀態暴露給用戶。相關邏輯一般體現在聚合根內。
    3)劃定事務的合理邊界。在某種程度上,劃定聚合就是劃定事務邊界。如果事務范圍過大,鎖定機制會導致多個用戶之間毫無意義的互相干擾,而如果事務范圍過小,會產生實時一致性問題,有把不完整的模型狀態呈現給客戶的風險。并不是強求時刻一致性就好,因為有性能代價,但無視客戶體驗也并非良策。聚合代表著兩者的平衡。
    4)聚合根提供了業務操作入口和界面。前三點偏技術,這一點對領域專家來說可能是最重要的。
    聚合對于使用者來說是一個“整體”,不能把其中的成員當作獨立的成員來看待。

  • 聚合內的約束一般歸納為以下6條:
    1)只有聚合根具有全局標識,它最終負責領域內在規則的檢查。有標識意味著聚合根必須是一個實體,而不是值對象。聚合根負責校驗聚合中的規則而不是其他成員或在聚合外部。
    2)聚合范圍內的實體具有本地標識,這些標識不需要全局范圍內唯一,保證在聚合范圍內唯一即可。為何不需要全局標識呢?這是由第三條約束決定的。
    3)只能通過聚合根才能訪問聚合內的其他元素,聚合根是其所在聚合所有模型的唯一操作界面。不要略過訂單去操控支付對象,不要忽略車輛而去保養輪胎,不要省掉棋盤去訪問棋子,不要不看新聞就去看評論。聚合保證了對象存在的意義,避免產生不符合邏輯的操作。這些例子中,脫離聚合根去訪問對象是沒有實際意義的。
    聚合根可以把對內部實體的引用傳遞給外部,但只是臨時使用這些引用,而不能一直保持引用。這意味著外部獲得的是內部對象的一個副本,對這個副本的修改絲毫不影響聚合內真實的對象。
    4)只有聚合根才能直接通過數據庫查詢獲取,其他對象必須通過遍歷關聯發現。當然,聚合的獲取一般通過工廠和存儲庫
    5)刪除根元素,必須一次刪除聚合內所有對象。這個問題在有垃圾回收機制的語言中不用過分操心,因為缺少外部引用,當聚合根被刪除之后,其他對象會被自動回收。
    6)當聚合內部任何對象被修改時,整個聚合內的所有規則都必須被滿足。這里不是指有延遲的最終一致性,而是任何時間點都不存在中間狀態。

  • 7條聚合設計法則
    (1)與生命周期保持一致
    如果一個部分脫離整體后不再有業務意義,那么該部分應當屬于一個聚合;如果一個模型脫離了聚合根仍有其業務價值,那么它就不屬于該聚合。比如,發動機與汽車雖然存在明顯的從屬關系,但因為發動機有自己的編號且脫離汽車還會被單獨跟蹤,所以它不屬于汽車這個聚合。當然,是不是一個聚合不會影響汽車和發動機正常的關聯關系
    (2)圍繞領域內在邏輯
    在定義聚合時,需要識別出滿足一個用例共同工作的對象組合,并且這些對象彼此必須時刻保持一致以滿足業務用例
    (3)與事務的粒度保持一致
    (4)不濫用聚合
    (5)作用范圍宜小不宜大
    大的聚合會影響性能。成員數據在聚合被創建時都需要從數據庫中加載,當集合成員快速增長時,對內存的占用也不可忽視
    小的聚合不僅有利于性能,還有助于事務的成功執行,可以減少事務提交的沖突,系統的可用性也獲得了提升(不再鎖定資源)
    (6)通過標識符引用其他聚合
    聚合是小巧的,但關聯是豐富的。一個聚合可以引用另一個聚合的聚合根來完成自己的任務。但被引用的聚合根不應該放在引用聚合的內部,這不符合聚合的規則。另外,在引用聚合根時,應該優先考慮全局唯一標識(ID)而不是通過直接的對象引用
    (7)利用最終一致性更新其他聚合
    聚合內的規則是時刻不能被違反的,但聚合間的規則要弱一些,所以跨越多個聚合的規則不立刻保持一致是可以接受的(容忍時長取決于業務),我們可以應用最終一致性規則來更新其他聚合
    最終一致性意味著不同聚合間的數據在一段時間內會存在一定的不一致狀態,但它們最終會保持一致,否則就不符合規則了

  • 實現方法

  • (1)如何限定訪問
    盡量不要開放聚合內部成員的可見性為public。如果確實需要引用,也不要開放該屬性的設置(set)功能,可以設為私有可見性,從而保護其不被修改。

  • (2)如何驗證規則
    驗證規則是在聚合根完成的,保證規則的方法就是封裝聚合的責任和行為,在對應的業務操作和方法中驗證領域內在規則,而不是暴露成員

  • (3)如何同生共消亡
    往往采用工廠方法來實現同生。工廠一次性創建聚合的所有成員,并且按照一定規則組裝。工廠可以放置在聚合根內,也可以是一個單獨的領域服務。
    不要為聚合成員提供單獨的構造函數,理論上也不應將聚合組裝的權力交給用戶,因為組裝體現的是領域邏輯而不是給用戶的自由。聚合成員最好只依賴于工廠而被創建,避免被越級使用而產生副作用。
    共消亡機制要用到存儲庫模式,存儲庫將只為客戶端提供對聚合根的訪問,我們無法通過存儲庫去訪問不是聚合根的對象,也就不會越過聚合根獲得其成員的引用。
    在有垃圾回收機制的語言中,沒有引用的對象會被定時回收,比如Java、C#。當然,也可以在聚合根使用析構函數或實現IDispose接口,來顯式釋放其他成員占用的資源。

  • (4)實現事務一致性
    第一,不要在一個事務中更新一個以上的聚合。
    第二,聚合作為一個整體,持久化時必須在一個事務中以保證內在的一致性。聚合是存儲在一個表或多個表中并不重要,重要的是當聚合被持久化時,需要在單個事務中提交,以確保在持文化失敗時,聚合不會以不一致的狀態被存儲。
    實現事務一致性很大程度上依賴于所采用的持久化技術。ORM(Object Relational Mapping,對象關系映射)框架如Hibernate和NHibernate提供了對幾乎一切數據庫命令的顯式事務的支持。

  • (5)實現最終一致性
    實現最終一致性保證的是聚合間的一致性,而不是聚合內的一致性。常見策略是采用異步方法來處理數據不一致問題,這與本地事務有所區別。雖然異步方法會導致聚合間數據不一致的時間較長,但其實現更為可靠且不會對并發性能產生影響。之所以說更為可靠,是因為當操作失敗時通常可以重試,但必須采用kafka、MQ等消息傳遞技術。
    聚合間的最終一致性可以通過異步領域事件來實現,使用EventBus等事件發布和訂閱框架可以很方便地發布和訂閱領域事件。

** 模型裝配線:工廠**

  • 在DDD中,工廠是生產領域模型的地方,特別是聚合

  • 工廠模式的主要目的是生產對象的實例并提供給調用者

  • 為什么需要工廠
    (1)解耦:分離領域職責與創建工序
    工廠的主要目的是分離模型的領域職責及其復雜的創建工序
    (2)通用語言:讓創建過程體現業務含義
    工廠是領域層中承載領域邏輯的對象
    (3)驗證:確保所創建的聚合處于正確狀態
    在工廠中創建新對象時,可以添加邏輯驗證以確保創建出的聚合符合領域內在規則。
    例如,在聚合根賬戶上創建訂單,要滿足賬戶必須有足夠的信用額度
    (4)多態:為一種接口生產多個組件
    (5)重建:重建已存儲的對象
    基于持久化機制的對象重建一定要封裝在工廠之內。
    重建工廠與創建新對象的工廠有以下兩個不同點:
    1)創建實體對象時,新對象是生成新的標識符,而重建工廠則是獲取已有的標識符ID。
    2)模型或聚合的內在領域邏輯不滿足時,新對象工廠可以直接拒絕生成對象,而重建工廠生成的對象違背規則時,需要設計師采用一種糾錯機制,比如默認值等策略來處理沖突。

  • 廠址選擇
    (1)聚合根上的工廠
    如果往一個聚合內添加元素,可以在聚合根上添加一個工廠方法,這樣聚合內部的元素的生成細節,外部就無須關心了。同時,因為聚合的內在原則檢查都在聚合根內,所以可以保證添加的元素都符合領域內在規則
    (2)“信息專家”工廠
    信息專家模式是把職責分配給具有完成該職責所需信息的那個模型
    (3)領域服務類工廠
    將工廠單獨地構建為領域服務是一種不錯的方法,也是最常用的工廠形式
    (4)只需使用構造函數的場合
    是否任何模型的創建都要經過工廠呢?恰恰相反,我們應該優先使用構造函數而不是工廠,因為領域模型并不一定都是復雜對象或聚合。如果在不需要解耦、不需要創建聚合、不需要表達通用語言、沒有內在規則或不需要多態的場合,應該直接使用構造函數new,因為構造函數更簡單、方便
    若滿足以下條件,則可直接選擇簡單的、公共構造函數。
    對象是值對象,且不是任何相關層次結構的一部分,而且不需要創建對象多態性。
    客戶關心的是具體類,而不是只關心接口。
    客戶可以訪問對象的所有屬性,且模型沒有嵌套對象的創建。
    構造環節并不復雜,客戶端創建代價不高。
    構造函數必須滿足工廠的相同規則:創建過程必須是一個原子操作,且能滿足領域內在規則。
    (5)廠名選擇
    1)選擇與領域含義相關的命名,如BookTicket(預訂車票)、ScheduleMeeting(安排會議)。
    2)將Create與要創建的類型名連在一起,以此來命名工廠方法,如CreateWhite-Board。
    3)將創建的類型名與Factory連接在一起,以此來命名工廠類型。例如,可以將創建Role對象的工廠類型命名為RoleFactory。
    模型貨架:存儲庫

  • 領域模型要想保持自己的獨立性,離不開存儲庫將其與持久化機制解耦

  • 存儲庫承擔了4個角色:隔離墻、冰箱和菜單、體現通用語言和管理員

  • (1)隔離墻:隔離領域模型與持久化技術,保證領域模型獨立性
    存儲庫模式的第一個意義在于保持領域模型與技術持久化機制的分離。這保證了領域模型的獨立性,使模型能夠在不受底層技術影響的情況下進行演化,讓我們可以獨立開發領域模型,無須關注架構的技術細節
    在這里插入圖片描述

  • 存儲庫分為兩部分
    1)存儲庫接口:位于領域層內,既有標準化的方法,如增加和刪除,又有體現通用語言的、業務上、特殊的檢索需求。
    2)存儲庫實現:位于基礎設施層,實現存儲庫接口。圖6-7是一個典型的依賴倒置架構,領域模型無須關心存儲庫的實現部分,而只需要和存儲庫接口打交道即可。

  • 2)冰箱和菜單:保鮮且提供菜肴而不是食材
    存儲庫要負責所有對象的持久化工作,同時提供這些對象的訪問接口。如果把模型比作一道道菜,存儲庫就是存放這些菜的冰箱,當你下次使用它們時,依然保持著上次放入冰箱的狀態。存儲庫要保證只保存和提供成品菜肴,也就是聚合
    一個聚合提供一個存儲庫,即一個聚合對應一個存儲庫,代碼實現上也是一對一存在的

  • (3)體現通用語言:讓檢索請求體現業務含義
    除了通用的接口方法,每個聚合的存儲庫必須支持通用語言的特定查詢。存儲庫不僅是CRUD接口,還是領域模型的擴展,應當以領域專家的術語來編寫,應當基于用例實現來構建查詢接口,而不是類似于CRUD數據訪問的角度來構建。

  • (4)管理員:集合的統計與匯總
    作為模型倉庫,存儲庫的另一個實用的功能是扮演倉庫管理員,給我們提供關于集合的統計信息,比如對象的數量、集合中所有匹配對象的某個數值屬性的總和等。此時,存儲庫提供的方法不再返回一個聚合根,而是一個值對象。對于“最好貼近原始數據進行計算”的計算原則來說,存儲庫的這個功能非常強大

  • 實現存儲庫接口時,我們要注意以下幾點:
    要把Add()方法實現為冪等,即使向集合重復添加相同聚合(一般由ID判斷),實際效果僅為一個聚合實例。
    如果持久化機制支持對對象變化的跟蹤,那么它會自動將內存中的更改保存到數據庫中,比如Hibernate框架,在存儲庫接口定義中不需要添加Save()方法。但如果持久化機制不支持對變化的跟蹤,則上述接口中需要添加Save()方法,并在領域對象被改變后,顯式調用該方法,以使更改在數據庫中生效。
    不要在領域模型上維護已更改的標識符,也不要讓持久化邏輯干擾領域模型的純潔性。將跟蹤變化的工作交由存儲庫來處理。

  • 存儲庫與工廠的設計出發點有些相似,但有區別。:
    1)負責模型生命周期的不同階段。工廠負責模型生命周期的開始,而存儲庫管理模型生命周期的中間和結束。工廠的作用之一是重建已存儲的對象,這與存儲庫有些類似,但此時對象還不存在或未成型,因此具有“新建”的含義。而在存儲庫中的對象,則讓用戶感覺是一直存在于內存集合中的既有對象。
    2)存儲庫專注于封裝持久化機制,而工廠專注于創建新對象。工廠用數據和領域邏輯來初始化和裝配一個復雜的對象(聚合)。新對象創建好之后,需要調用存儲庫的Add()方法將其添加到存儲庫中,由存儲庫負責其在數據庫中的存儲。
    3)工廠更注重于對象創建的多態機制,而存儲庫分為兩個部分,接口部分更注重符合業務需求和通用語言的支持,實現部分的重點則是對持久化技術的封裝。

  • 存儲庫與數據訪問對象的區別
    存儲庫面向的是內存中的集合方式,而DAO面向數據庫表提供CRUD操作。
    存儲庫的設計更加貼近領域,而DAO中方法的業務意圖并不明顯。
    存儲庫接口必須位于領域層內,且只提供聚合根的訪問,DAO沒有這種約束。

  • 存儲庫實現的注意事項
    (1)不要提供無條件隨機查詢接口
    (2)可以像工廠一樣在存儲庫中使用多態,返回子類或實現類
    (3)充分利用存儲庫接口與實現解耦的特點
    (4)存儲庫中不要涉及事務

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/38268.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/38268.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/38268.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CSDN寫文章時需要上、下標字號怎么輸?

上標:?^^,符號中間加字 下標:~~,符號中間加字 前題是用MD編輯器,不然白搭: 我是感覺CSDN這個文本編輯比較拉,非常想吐槽。

dB分貝入門

主要參考資料: dB(分貝)定義及其應用: https://blog.csdn.net/u014162133/article/details/110388145 目錄 dB的應用一、聲音的大小二、信號強度三、增益 dB的應用 一、聲音的大小 在日常生活中,住宅小區告知牌上面標示噪音要低…

vue2 element ui 表單 動態增加表單項 表單項值不可重復 select多選

案例 <template><el-form :model"form" ref"form" label-width"70px"><el-form-item><el-button icon"el-icon-plus" type"primary" plain click"add">新增</el-button><el-b…

VUE3-Elementplus-form表單-筆記

1. 結構相關 el-row表示一行&#xff0c;一行分成24份 el-col表示列 (1) :span"12" 代表在一行中&#xff0c;占12份 (50%) (2) :span"6" 表示在一行中&#xff0c;占6份 (25%) (3) :offset"3" 代表在一行中&#xff0c;左側margin份數 el…

后勞動經濟學(PLE):AI時代的工作未來

引言 隨著人工智能&#xff08;AI&#xff09;和自動化技術的飛速發展&#xff0c;我們迎來了一個新的經濟范式——后勞動經濟學&#xff08;PLE&#xff09;。這一概念主要討論在AI和自動化技術超越人類能力的關鍵領域后&#xff0c;機器將不可避免地在許多經濟活動中取代人類…

如何玩單機版:QQ音速

前言 我是研究單機的老羅&#xff0c;今天教大家帶來一款懷舊游戲QQ音速 的教程。根據我的文章&#xff0c;一步一步就可以玩了。 如今市面上的資源參差不齊&#xff0c;大部分的都不能運行&#xff0c;本人親自測試&#xff0c;運行視頻如下&#xff1a; QQ音速 搭建教程 此…

python之GIL鎖詳解

目錄 1.GIL是什么以及影響 2.為什么會有GIL鎖&#xff1f; 1.GIL是什么以及影響 在Python中&#xff0c;多線程的并發性受到全局解釋器鎖&#xff08;GIL&#xff0c;Global Interpreter Lock&#xff09;的影響。GIL是CPython&#xff08;Python的官方實現&#xff09;中的…

vscode下無法識別node、npm的問題

node : 無法將“node”項識別為 cmdlet、函數、腳本文件或可運行程序的名稱 因為node是在cmd安裝的&#xff0c;是全局安裝的&#xff0c;并不是在這個項目里安裝的。 解決方案&#xff1a; 1.在vscode的控制臺&#xff0c;針對一個項目安裝特定版本的node&#xff1b; 2.已經…

C++(Python)肥皂泡沫普拉托邊界膜曲面模型算法

&#x1f3af;要點 &#x1f3af;肥皂泡二維流體模擬 | &#x1f3af;泡沫普拉托邊界膜曲面模型算法演化厚度變化 | &#x1f3af;螺旋曲面三周期最小結構生成 &#x1f4dc;皂膜用例&#xff1a;Python計算物理粒子及拉格朗日和哈密頓動力學 | Python和MATLAB粘性力接觸力動…

SAP系統中的應收賬款(客戶主數據,日常交易,催收,爭議管理)

1. 客戶主數據 Customer Accounts (客戶賬戶&#xff09;:客戶賬戶由兩部分General Data&#xff08;通用數據&#xff09;和Company Code Data&#xff08;公司代碼段數據&#xff09;組成。通用數據是在client級別獨立于公司代碼的數據&#xff0c;例如客戶的地址&#xff0…

鴻蒙開發設備管理:【@ohos.multimodalInput.inputEventClient (注入按鍵)】

注入按鍵 InputEventClient模塊提供了注入按鍵能力。 說明&#xff1a; 本模塊首批接口從API version 8開始支持。后續版本的新增接口&#xff0c;采用上角標單獨標記接口的起始版本。本模塊接口均為系統接口&#xff0c;三方應用不支持調用。 導入模塊 import inputEventCli…

愛情再啟:莊國棟笑談“玫瑰人生”愛情覺悟

莊國棟&#xff0c;這位電視劇《玫瑰的故事》中的男主角&#xff0c; 最近在一次采訪中坦言&#xff1a;“如果給我一次重來的機會&#xff0c; 我絕對會毫不猶豫地選擇愛情&#xff01;” 聽到這話&#xff0c; 我不禁想&#xff0c;莊先生&#xff0c;您是不是被劇里的玫瑰…

Solidworke學習(裝配體3)

目錄 本節學習內容&#xff1a; 一、高級配合 &#xff08;1&#xff09;對稱配合 &#xff08;2&#xff09;寬度配合 &#xff08;3&#xff09;距離配合 二、機械配合 &#xff08;1&#xff09;凸輪配合 &#xff08;2&#xff09;槽口配合 三、快捷菜單 本節學習…

python工作目錄與文件目錄

工作目錄 文件目錄&#xff1a;文件所在的目錄 工作目錄&#xff1a;執行python命令所在的目錄 D:. | main.py | ---data | data.txt | ---model | | model.py | | train.py | | __init__.py | | | ---nlp | | | bert.py | …

計算機網絡期末復習(大題+小題)

計算機網絡期末復習 一、計算機網絡概述 Point 1 計算機網絡就是以傳輸信息為基本目的&#xff0c;用通信線路和通信設備將多個計算機連接起來的計算機系統的集合。由自治的計算機互聯起來的結合體。 Point 2 按網絡的覆蓋范圍進行分類 &#xff08;1&#xff09;局域網*…

解鎖Transformer的魯棒性:深入分析與實踐指南

&#x1f6e1;? 解鎖Transformer的魯棒性&#xff1a;深入分析與實踐指南 Transformer模型自從由Vaswani等人在2017年提出以來&#xff0c;已經成為自然語言處理&#xff08;NLP&#xff09;領域的明星模型。然而&#xff0c;模型的魯棒性——即模型在面對異常、惡意或不尋常…

人機交互新維度|碩博電子發布雙編碼器操作面板、無線操作面板等新品

6月15日&#xff0c;碩博電子召開了一場新品發布會&#xff0c;向業界展示了多項前沿技術成果&#xff0c;其中備受矚目的當屬SPM-KEYP-D08雙編碼器操作面板、SPM-KEYP-D16W無線操作面板、SPR-HT-XK12A無線手持發射端以及SPQ-WT-B01灑水車專用控制面板。這些創新產品的亮相&…

文心一言 VS 訊飛星火 VS chatgpt (292)-- 算法導論21.3 5題

五、證明&#xff1a;任何具有 m 個 MAKE-SET、UNION 和 FIND-SET 操作的序列&#xff0c;這里所有的 LINK 操作都出現在 FIND-SET 操作之前&#xff0c;如果同時使用路徑壓縮和按秩合并啟發式策略&#xff0c;則這些操作只需 O(m) 的時間。在同樣情況下&#xff0c;如果只使用…

Class Constructors and Destructors (類的構造函數和析構函數)

Class Constructors and Destructors [類的構造函數和析構函數] 1. Declaring and Defining Constructors (聲明和定義構造函數)2. Using Constructors (使用構造函數)3. Default Constructors (默認構造函數)4. Destructors (析構函數)5. Improving the Stock Class (改進 Sto…

MT1597 平行線

題目 用下面的數據類型表示線&#xff1a; struct POINT { //點 int x, y; //坐標值x和y } ; struct LINE { //線 POINT s, e; //線的兩端 } ; 輸入2個線段的端點的坐標值x和y&#xff0c;判斷兩條線段所在直線是否為平行線。如果兩線段共線&#xff0c;判為不平行。 輸入…