設(shè)計(jì)業(yè)務(wù)實(shí)例
辦公用品采購(gòu)系統(tǒng)。
- 企業(yè)的員工可以通過該系統(tǒng)提交一個(gè)采購(gòu)請(qǐng)求剖张,一個(gè)請(qǐng)求包含了若干數(shù)量侧甫、若干類型的辦公用品(稱為采購(gòu)項(xiàng))迫像。
- 主管負(fù)責(zé)對(duì)采購(gòu)申請(qǐng)進(jìn)行審批。
- 審批通過后潭袱,系統(tǒng)會(huì)根據(jù)提供商不同,生成若干訂單谒养。
如果
采用以數(shù)據(jù)庫(kù)為中心的建模方式
第一股淡,對(duì)于模型的討論過早地進(jìn)入了實(shí)現(xiàn)領(lǐng)域身隐,和業(yè)務(wù)概念脫開了聯(lián)系,不便于持續(xù)地和業(yè)務(wù)人員協(xié)作唯灵;
第二贾铝,技術(shù)細(xì)節(jié)和業(yè)務(wù)規(guī)則的細(xì)節(jié)糾纏在一起,很容易顧此失彼面向?qū)ο?br> 業(yè)務(wù)規(guī)則如何保證埠帕,在傳統(tǒng)的面向?qū)ο蠓椒ㄖ胁]有嚴(yán)格的實(shí)現(xiàn)約束
針對(duì)類似“修改采購(gòu)項(xiàng)也是修改采購(gòu)請(qǐng)求”的需求忌傻,DDD的做法
把采購(gòu)請(qǐng)求和采購(gòu)項(xiàng)組織到一起,看做一個(gè)更大的整體搞监,稱為“聚合”。
這個(gè)聚合內(nèi)部的業(yè)務(wù)邏輯[例如“采購(gòu)申請(qǐng)審核通過后镰矿,不得對(duì)采購(gòu)申請(qǐng)條目進(jìn)行更改”]應(yīng)內(nèi)建于聚合內(nèi)部琐驴。
為了實(shí)現(xiàn)這一目標(biāo),我們約定:對(duì)采購(gòu)項(xiàng)(實(shí)體)的一切操作(增加秤标、刪除绝淡、修改等),都是對(duì)采購(gòu)請(qǐng)求(聚合根)的操作苍姜。
聚合定義
將實(shí)體和值對(duì)象劃分為聚合并圍繞著聚合定義邊界牢酵。選擇一個(gè)實(shí)體作為每個(gè)聚合的根,并僅允許外部對(duì)象持有對(duì)聚合根的引用衙猪。作為一個(gè)整體來定義聚合的屬性和不變量馍乙,并把其執(zhí)行責(zé)任賦予聚合根或指定的框架機(jī)制。
https://www.domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf
聚合本質(zhì)
- 建立一個(gè)比對(duì)象粒度更大的邊界
- 聚集緊密關(guān)聯(lián)的對(duì)象 ---> 形成了一個(gè)業(yè)務(wù)上的對(duì)象整體垫释。
- 使用聚合根作為對(duì)外的交互入口 --> 保證多個(gè)互相關(guān)聯(lián)的對(duì)象的一致性丝格。
合理使用聚合,可以更容易地保證業(yè)務(wù)規(guī)則的一致性棵譬,減少了對(duì)象之間可能的耦合显蝌,提升設(shè)計(jì)的可理解性,降低出問題的可能性订咸。
聚合劃分的幾個(gè)啟發(fā)式規(guī)則
- 生命周期一致性
- 問題域一致性
- 場(chǎng)景頻率一致性
- 盡量小的聚合
生命周期一致性
如果聚合根消失曼尊,聚合內(nèi)的其他元素都應(yīng)該同時(shí)消失。
例
如果采購(gòu)請(qǐng)求(聚合根)不存在了脏嚷,那么采購(gòu)項(xiàng)當(dāng)然也就失去了存在的意義骆撇。而商品、作為申請(qǐng)人的用戶等對(duì)象父叙,和采購(gòu)請(qǐng)求之間則不存在此關(guān)系艾船。
則商品葵腹、作為申請(qǐng)人的用戶等對(duì)象不納入采購(gòu)請(qǐng)求
問題域一致性
聚合表示的模型一定會(huì)位于同一個(gè)限界上下文之內(nèi)
例
一個(gè)在線論壇,用戶可以對(duì)論壇上用戶的文章發(fā)表評(píng)論屿岂。文章顯然應(yīng)該是一個(gè)聚合根践宴。如果文章被刪除,那么爷怀,用戶的評(píng)論看起來也要同時(shí)消失阻肩。那么評(píng)論是否可以屬于文章這個(gè)聚合?
一個(gè)圖書網(wǎng)站运授,用戶可以對(duì)圖書發(fā)表評(píng)論烤惊,那么評(píng)論是否可以屬于圖書這個(gè)聚合?
評(píng)論這一個(gè)概念吁朦,在本質(zhì)上和文章/圖書這個(gè)概念相去甚遠(yuǎn)柒室,因此不屬于同一個(gè)問題域的對(duì)象
則不應(yīng)該出現(xiàn)在同一個(gè)聚合中
需要依賴”最終一致性“來實(shí)現(xiàn)聚合之間的一致性。例如逗宜,在文章刪除的時(shí)候雄右,發(fā)送一個(gè)文章刪除的消息。評(píng)論系統(tǒng)接收到文章刪除消息之后纺讲,刪除文章對(duì)應(yīng)的評(píng)論擂仍。
場(chǎng)景頻率一致性
場(chǎng)景(scenario)
- 是業(yè)務(wù)用例的具體化描述
- 反映了用戶使用系統(tǒng)達(dá)成業(yè)務(wù)目標(biāo)的方式。
經(jīng)常被同時(shí)操作的對(duì)象熬甚,它們往往屬于同一個(gè)聚合逢渔。而那些極少被同時(shí)關(guān)注的對(duì)象,一般不應(yīng)該劃為一個(gè)聚合乡括。
例
考慮軟件開發(fā)中的產(chǎn)品和版本以及功能的關(guān)系肃廓。產(chǎn)品和版本算不算是同一個(gè)問題域?
操作場(chǎng)景不一致的對(duì)象诲泌,或者說如果一個(gè)對(duì)象在不同場(chǎng)景下都會(huì)被使用亿昏,應(yīng)該考慮把它們分到不同的聚合中。
盡量小的聚合
凡是不破壞以上三個(gè)一致性的情況档礁,都沒有必要把它們放到同一個(gè)聚合中
聚合之間如何關(guān)聯(lián)?
例
采購(gòu)申請(qǐng)及其屬性(如狀態(tài)角钩、提交時(shí)間等)以及采購(gòu)項(xiàng)屬于一個(gè)聚合。但是呻澜,商品递礼、用戶(提交人,審批人...)這些不能屬于采購(gòu)申請(qǐng)這個(gè)聚合羹幸。聚合之間如何關(guān)聯(lián)?
引入ID對(duì)象來解決這個(gè)問題
引入ID對(duì)象帶來的問題
- 某些場(chǎng)景下需要對(duì)信息進(jìn)行第二次查詢脊髓,而且無法利用 ORM 的 EagerFetch/LazyFetch 加載機(jī)制的遍歷
這不是損失,這類問題應(yīng)該由外部服務(wù)栅受,例如應(yīng)用層服務(wù)來完成将硝。
好處恭朗,斷開聚合,加快查詢速度 - 為了斷開聚合而額外引入的 Id 值對(duì)象依疼,還能算是領(lǐng)域模型或者是 “統(tǒng)一語言” 的一部分嗎痰腮?
這是 DDD 的實(shí)現(xiàn)機(jī)制的一部分,它屬于領(lǐng)域模型律罢,但是請(qǐng)把其可見性控制在開發(fā)團(tuán)隊(duì)膀值。沒有必要和業(yè)務(wù)人員溝通這些概念 - 注意
這個(gè) Id 對(duì)象引用的只能是其他聚合根的 Id
聚合根的 ID 應(yīng)該做到全局唯一
聚合內(nèi)部的實(shí)體對(duì)象/值對(duì)象,保證內(nèi)部的 ID 唯一即可误辑。
代碼實(shí)現(xiàn)方面的考慮
資源庫(kù)(Repository)沧踏、工廠(Factory)面向聚合定義
- 使用工廠來構(gòu)造聚合對(duì)象是一種更好的對(duì)復(fù)雜性的封裝
在聚合以外,只應(yīng)該有一個(gè)工廠對(duì)外可見巾钉,那就是聚合的工廠 - 資源庫(kù)是聚合的倉(cāng)儲(chǔ)機(jī)制
一個(gè)聚合只能有一個(gè)資源庫(kù)對(duì)象翘狱,那就是以聚合根命名的資源庫(kù)。除此之外的其他對(duì)象砰苍,都不應(yīng)該提供資源庫(kù)對(duì)象潦匈。
代碼結(jié)構(gòu)與聚合保持一致
限界上下文,模塊
└━聚合
???????└━實(shí)體(包含聚合根)對(duì)象师骗、值對(duì)象、資源庫(kù)讨惩、工廠
聚合不可跨越部署的邊界
- 如果系統(tǒng)采用了微服務(wù)架構(gòu)辟癌,應(yīng)該保持部署邊界和限界上下文邊界的一致
不要讓部署的粒度大于限界上下文的粒度,這樣可以帶來更好的業(yè)務(wù)靈活性和可伸縮性 - 從服務(wù)的最小邊界上荐捻,不可讓最小邊界小于聚合的粒度黍少,否則會(huì)帶來大量的數(shù)據(jù)的一致性問題
因?yàn)槲⒎?wù)之間的一致性一般需要通過最終一致性來保證,如果聚合跨越了部署邊界將會(huì)是一致性的災(zāi)難
聚合改進(jìn)了系統(tǒng)性能和可伸縮性
使用小的聚合
每個(gè)涉及訪問的對(duì)象(事實(shí)上就是聚合)不可能很大处面,而所需的數(shù)據(jù)又恰如其分的都在厂置,數(shù)據(jù)完整性和業(yè)務(wù)完整性就有了保障,還可以方便地進(jìn)行水平擴(kuò)展魂角,性能和可伸縮性也就同時(shí)得到了滿足昵济。