日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何运用领域驱动设计 - 聚合

發(fā)布時(shí)間:2023/12/4 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何运用领域驱动设计 - 聚合 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

概述

  • DDD實(shí)戰(zhàn)與進(jìn)階 - 值對(duì)象

  • 如何運(yùn)用DDD - 實(shí)體

  • 如何運(yùn)用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì) - 領(lǐng)域服務(wù)

在前幾篇的博文中,我們已經(jīng)學(xué)習(xí)到了如何運(yùn)用實(shí)體和值對(duì)象。隨著我們所在領(lǐng)域的不斷深入,領(lǐng)域模型變得逐漸清晰,我們已經(jīng)建立了足夠豐富的實(shí)體和值對(duì)象。但隨著實(shí)體和值對(duì)象的數(shù)量逐漸增多,它們之間的關(guān)系也顯得越來越復(fù)雜:實(shí)體A與實(shí)體B存在一對(duì)一的關(guān)系,實(shí)體B又與實(shí)體C存在一對(duì)多的關(guān)系。就這樣一層套一層,本來約束已經(jīng)足夠好的領(lǐng)域?qū)ο髠冡莘鹨呀?jīng)開始對(duì)我們不太友好。為了處理這一系列的問題,我們需要將一些實(shí)體和值對(duì)象劃分在一個(gè)統(tǒng)一的邊界內(nèi),原來存在多重關(guān)聯(lián)關(guān)系的大模型被分解為較小的領(lǐng)域?qū)ο笕骸?/p>

而這種強(qiáng)有力的劃分手法就是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)戰(zhàn)術(shù)模式中的“聚合”。可能大家已經(jīng)聽過它的一個(gè)重要部分“聚合根”,那么我們什么情況下考慮使用聚合根呢?聚合根又是從什么地方來?聚合與實(shí)體之間又有什么關(guān)系?如何確定和劃分一個(gè)合理的聚合?本文將從不同的角度來帶大家重新認(rèn)識(shí)一下“聚合”這個(gè)概念,并且給出相應(yīng)的代碼片段(本教程的代碼片段都使用的是C#,后期的實(shí)戰(zhàn)項(xiàng)目也是基于?DotNet Core?平臺(tái))。

何為聚合

還是先來看看原著《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):軟件核心復(fù)雜性應(yīng)對(duì)之道》?中對(duì)聚合的有關(guān)解釋:

在具有復(fù)雜關(guān)聯(lián)的模型中要想保證對(duì)象更改的一致性是很困難的。不僅互不關(guān)聯(lián)的對(duì)象需要遵守一些固定規(guī)則,而且緊密關(guān)聯(lián)的各組對(duì)象也要遵守一些固定規(guī)則。然而,過于謹(jǐn)慎的鎖定機(jī)制又會(huì)導(dǎo)致多個(gè)用戶之間臺(tái)無意義地互相干擾,從而使系統(tǒng)不可用。
首先,我們需要用一個(gè)抽象來封裝模型中的引用。AGGREGATE就是一組相關(guān)對(duì)象的集合,我們把它作為數(shù)據(jù)修改的單元。每個(gè)AGGREGATE都有一個(gè)根(root)和一個(gè)邊界(boundary).邊界定義了AGGREGATE的內(nèi)部都有什么。根則是AGGREGATE中所包含的一個(gè)特定Entity。在AGGREGATE中,根是唯一允許外部對(duì)象保持對(duì)它的引用的元素,而邊界內(nèi)部的對(duì)象之間則可以互相引用。除根以外的其他Entity都有本地標(biāo)識(shí),但這些標(biāo)識(shí)只有在AGGREGATE內(nèi)部才需要加以區(qū)別,因?yàn)橥獠繉?duì)象除了根Entity之外看不到其他對(duì)象。

演化案例

還記得我們?cè)谏弦黄┪?如何運(yùn)用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì) - 實(shí)體?中所展開的一個(gè)關(guān)于旅行記賬的案例嗎?在學(xué)習(xí)實(shí)體的時(shí)候,我們已經(jīng)構(gòu)建了一個(gè)叫做Itinerary的實(shí)體,并且賦予了它應(yīng)用的行為操作。到目前為止,我們那個(gè)案例好像還和主題離的稍微有點(diǎn)遠(yuǎn),我們雖然實(shí)現(xiàn)了行程這個(gè)東西,但是怎么記賬呢?

接下來,讓我們完善這個(gè)案例,讓它更貼近于我們真實(shí)的項(xiàng)目需求:

當(dāng)用戶創(chuàng)建一個(gè)行程時(shí),則證明該旅程的賬單已經(jīng)被開啟了。創(chuàng)建該行程的用戶被認(rèn)定為管理員,他可以添加參與該行程的小伙伴。所有參與行程的小伙伴,都可以在旅行的過程中記賬(比如小伙伴C和小伙伴A吃了一頓火鍋花了300塊錢,小伙伴C則可以記入本筆開銷,而該筆開銷的參與者是小伙伴C和A),當(dāng)大家旅行完成了之后就可以進(jìn)行結(jié)算,將費(fèi)用平攤到每個(gè)人身上,誰需要補(bǔ)錢,誰需要退錢等都可以被該應(yīng)用計(jì)算出來。

這是簡(jiǎn)化后的版本,為的是希望大家能大致明白我們需要做一個(gè)什么樣的東西,并且如何用我們所學(xué)到的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)知識(shí)來建模和編碼,為了讓大家更清晰的理解需求,我粗淺的為大家繪制了一個(gè)原型圖:

發(fā)現(xiàn)實(shí)體關(guān)系

根據(jù)需求描述,再結(jié)合我們已有的領(lǐng)域設(shè)計(jì)知識(shí),我們馬上就能找出另外一個(gè)重要的實(shí)體對(duì)象出來。沒錯(cuò),那就是賬單。在這個(gè)案例中,我們暫定將賬單命名為記賬薄Account book)。在第二個(gè)原型圖中,我們大致能夠理解記賬薄是一個(gè)什么東西,它記錄了行程中所有的開銷內(nèi)容和開銷金額。這一行一行的開銷信息,我們將它命名為開銷項(xiàng)Overhead item)。這里為了簡(jiǎn)化起見,我們忽略了每條開銷項(xiàng)中的其它信息,例如參與人員,參與地點(diǎn)等等。

接下來,我們來分析已經(jīng)發(fā)現(xiàn)的兩個(gè)事物:記賬薄?與?開銷項(xiàng)。先來說開銷項(xiàng)吧,它是屬于實(shí)體還是值對(duì)象呢?結(jié)合前兩篇博文中我們說學(xué)到的內(nèi)容,它需要一個(gè)ID來辨識(shí)它嗎?也許還是有些困惑,因?yàn)楹孟袼幌裥詣e、姓名這一類東西具有很明顯的無ID特征。所以我們需要來識(shí)別該對(duì)象擁有的屬性:開銷內(nèi)容、開銷金額、開銷時(shí)間。“在2019年10月12日,買了一個(gè)冰糕花費(fèi)了3元人民幣”,在我們當(dāng)前的領(lǐng)域,我們需要使用一個(gè)ID來區(qū)分它嗎?很顯然我們是需要的,我們不能說只要在同一時(shí)間花了同樣的錢買了同樣的東西就是一樣的東西了,比如用戶A在行程A中和用戶B在行程B中同時(shí)間同樣的錢買了同樣的東西,我們會(huì)認(rèn)為是一樣的嗎?很顯然,不能。所以開銷項(xiàng)是一個(gè)實(shí)體。那么記賬薄呢?很顯然,它也是一個(gè)實(shí)體,我們需要通過ID來識(shí)別到底是哪個(gè)記賬薄。

此時(shí)我們已經(jīng)捕獲出了兩個(gè)實(shí)體對(duì)象:記賬薄?與?開銷項(xiàng)。而且可以清楚的看到,它們之間是一個(gè)一對(duì)多的關(guān)系。然后來嘗試將它們轉(zhuǎn)換為我們熟悉的C#代碼吧:

復(fù)制代碼

public class AccountBook {public Guid ID { get; private set; }public List<OverheadItem> OverheadItems {private get;private set; }//ctor// 記賬薄的行為// .... }public class OverheadItem {public Guid ID { get; private set; }//開銷內(nèi)容public OverheadContent Content { get;private set; }//開銷金額public OverheadMoney Money { get;private set; }// ctor// 開銷項(xiàng)目的行為// .... }public class Itinerary {public int ID { get; set; }public List<Person> Participants { get;private set; }public List<Address> Places { get;private set; }public ItineraryNote Note { get;private set; }public ItineraryTime TripTime { get;private set; }public ItineraryStatus Status { get;private set; }//ctor// 行程的行為// .... }

OK,此時(shí)我們已經(jīng)完成了記賬有關(guān)的模型。再來回顧我們之前的行程實(shí)體模型:“當(dāng)旅程建立的時(shí)候,則證明該旅程的賬單已經(jīng)被開啟了”,因此我們可以看出,旅程和賬薄是連接在一起的,一個(gè)旅程就對(duì)應(yīng)著其擁有的對(duì)應(yīng)賬薄,所以它們是一個(gè)一對(duì)一的關(guān)系。

到目前為止,我們擁有了三個(gè)比較明顯的實(shí)體:旅程、記賬薄、開銷項(xiàng)目,還有該領(lǐng)域中很多大大小小的值對(duì)象。旅程和記賬薄是一對(duì)一的關(guān)系,記賬薄和開銷項(xiàng)目是一對(duì)多的關(guān)系。多讀一下它們之間的關(guān)聯(lián)關(guān)系,唉!!!好累,那是不是再引入一個(gè)領(lǐng)域?qū)ο筮M(jìn)來,就會(huì)讓它們之間的關(guān)系更復(fù)雜呢?這樣一層繞一層,就仿佛滾毛線球一樣,越理越亂了。

開始劃分邊界吧

我根據(jù)目前所涉及的領(lǐng)域?qū)ο?#xff0c;大致繪了一個(gè)領(lǐng)域之間的圖,當(dāng)然這個(gè)圖并不是規(guī)范的,里面缺少了很多我們已經(jīng)捕獲出來的值對(duì)象等等,它只是為了幫助你大致回顧一下我們目前所Get到的領(lǐng)域模型結(jié)果:

圖中將“旅行記賬”的部分于“推薦”的部分用了方塊給隔離開來,這個(gè)結(jié)果我想大家也很容易理解,因?yàn)橛嘘P(guān)推薦的這些東西,比如推薦餐館呀,推薦花店呀對(duì)我們的旅行記賬來說并沒有太大的關(guān)系。關(guān)系域于關(guān)系域中,我們通過劃分了一個(gè)合理的邊界來隔離它們,那么反過來思考,一個(gè)域中的各個(gè)領(lǐng)域?qū)ο?#xff0c;我們能不能通過一個(gè)什么手段來劃分它們呢?將它們通過邊界的隔離,實(shí)現(xiàn)區(qū)域內(nèi)的自治,這樣更方便我們來處理它們之間的邏輯關(guān)系。

假如用戶想查看當(dāng)前行程的記賬薄,按照常規(guī)處理我們會(huì)怎么辦呢?用戶會(huì)訪問有關(guān)記賬薄的倉(cāng)儲(chǔ)(倉(cāng)儲(chǔ)的有關(guān)概念將在下一篇文章講解),獲取到當(dāng)前記賬薄。此時(shí),用戶獲取到了賬薄的有關(guān)信息,比如開銷項(xiàng)啊,總開銷金額啊等等,但是對(duì)用戶來說,它是很迷茫的,因?yàn)樗鼉H僅獲取到了賬薄的信息,它不知道這個(gè)賬薄屬于哪次行程,所以它必須又得去獲取一下行程的信息。而這種場(chǎng)景往往都是一起出現(xiàn)的,你只要獲取賬薄你就必須要獲取行程。

可能你已經(jīng)發(fā)現(xiàn)了,它們其實(shí)可以是一體的。就像開銷項(xiàng)和記賬薄是一體的一樣,行程記賬薄這兩個(gè)大實(shí)體居然也是可以是一體的。而這種關(guān)系,就是我們今天的主題——“聚合”。

我們可以將旅行行程、記賬薄、行程人員、開銷項(xiàng)、行程時(shí)間等一系列有關(guān)的對(duì)象都劃分在行程的邊界內(nèi),因?yàn)榇_確實(shí)實(shí)它們是屬于行程的,一旦脫離了行程它們好像都沒有任何意義。

選取一個(gè)聚合根

行程記賬薄是一體的,且它們是一對(duì)一的關(guān)系。如果將這個(gè)關(guān)系轉(zhuǎn)換為我們熟悉的代碼,我們需要將一個(gè)類作為另一個(gè)類的屬性,那么在這個(gè)案例中,我們是用行程包含記賬薄,將記賬薄作為屬性呢?還是記賬薄包含行程呢?你也許會(huì)說,它們可以相互包含。確實(shí),現(xiàn)在的ORM框架可以運(yùn)行你將兩者互相包含并映射到數(shù)據(jù)庫(kù),但是在這里我們沒有必要這么做,因?yàn)槲覀円呀?jīng)知道,它們是一個(gè)整體,獲取一者另外一者同樣會(huì)被獲取到,不需要再次嵌套。回到剛才那個(gè)問題,是誰在外層呢?其實(shí)答案也很清晰了,因?yàn)閺脑摾觼碚f,我們更關(guān)注的是行程,所以我們很自然的就會(huì)將行程作為主要的實(shí)體對(duì)象,而在這個(gè)聚合關(guān)系中,被我們選取出來作為邊界范圍的實(shí)體就是我們所說的聚合根。

此時(shí)我們的代碼可能已經(jīng)可以改變成這樣了:

復(fù)制代碼

public class Itinerary {public int ID { get; set; }public List<Person> Participants { get;private set; }public List<Address> Places { get;private set; }public ItineraryNote Note { get;private set; }public ItineraryTime TripTime { get;private set; }public ItineraryStatus Status { get;private set; }//將記賬薄放置在了旅行中public AccountBook AccountBook{get;private set;}//ctor// 行程的行為// .... }

通過聚合根保護(hù)你的內(nèi)部對(duì)象

當(dāng)識(shí)別出了一個(gè)聚合根的時(shí)候,就要保證該聚合的內(nèi)部是自治的。我們不能從外界直接訪問聚合根內(nèi)部的任何領(lǐng)域?qū)ο?#xff0c;比如在上面的案例中,我們則不能直接記賬薄這個(gè)實(shí)體。如果我們確確實(shí)實(shí)需要獲取記賬薄中的有關(guān)信息,我們必須通過聚合根,也就是上面的行程來訪問。也就是說我們得從倉(cāng)儲(chǔ)中獲取行程后再來得到記賬薄的有關(guān)信息。

此時(shí),你可能會(huì)說,那這樣不就會(huì)很麻煩了嗎?我只要記一筆賬,但我必須要得到旅程的所有信息。這樣數(shù)據(jù)庫(kù)和應(yīng)用程序不是增加了一些壓力嗎?是的,這樣做我們會(huì)將更多的數(shù)據(jù)加載到內(nèi)存之中來。但是這是合理的,回顧剛才一下上面的案例,我們有什么情況下需要只獲取賬薄,不獲取旅程信息呢?是的,沒有,它們永遠(yuǎn)是一起出現(xiàn)的。

當(dāng)聚合內(nèi)部的對(duì)象無法直接訪問的時(shí)候,很顯然也不能直接調(diào)用該對(duì)象所公開出來的行為了。比如記賬薄可能會(huì)擁有一個(gè)叫做“記一筆賬(RecordAnAccount)”的行為,我們通過訪問該行為操作就可以將開銷項(xiàng)增加到記賬薄中。但是現(xiàn)在我們不能直接訪問記賬薄了,我們?cè)趺从涃~呢?通過轉(zhuǎn)移行為給聚合根來完成,比如我們會(huì)將該行為轉(zhuǎn)移到行程中,并公布一個(gè)叫做“記錄行程中一筆賬”的行為供客戶端調(diào)用。

復(fù)制代碼

public class Itinerary {//Other Property....public List<Person> Participants { get;private set; }public AccountBook AccountBook{get;private set;}//ctorpublic void RecordAnAccountInItinerary(int PersonID,string itemName,double costMoney){bool hasThisPerson = Participants.Any(Person=>Person.ID = PersonID);if(!hasThisPerson)throw new PersonNotInThisItineraryException();AccountBook.RecordAnAccount(itemName,costMoney);} }

這樣一來,聚合根內(nèi)部的所有對(duì)象都不會(huì)被外界肆意訪問,而且通過聚合根所表達(dá)出來的行為也更容易讓人能夠理解。

聚合的一些特性

到了現(xiàn)在,再回頭去看一下概述中原著對(duì)聚合概念的闡述。我們可以已經(jīng)大致理解了什么是聚合,聚合根又是怎么來的:

  • 聚合是一個(gè)明確的邊界

  • 聚合的出現(xiàn)是為了解決領(lǐng)域模型之間的復(fù)雜關(guān)聯(lián)關(guān)系的

  • 聚合封裝了一系列的相關(guān)對(duì)象,它是這些對(duì)象的集合

  • 聚合應(yīng)該有一個(gè)根,并且這個(gè)根是通過集合中的一個(gè)實(shí)體選出來的

  • 聚合外部的事物想引用聚合只能通過根的ID來訪問

再來給大家舉一個(gè)原著中的例子,加深印象:汽車修配廠的軟件可能會(huì)使用一個(gè)汽車模型。汽車是一個(gè)具有全局標(biāo)識(shí)的ENTITY:我們需要將這部汽車與世界上所有其他汽車區(qū)分開(即使是一些非常相似的汽車),我們可以使用車輛識(shí)別號(hào)來進(jìn)行區(qū)分,車輛識(shí)別號(hào)是為每輛新汽車分配的唯一標(biāo)識(shí)符。我們可能想跟蹤4個(gè)輪胎的歷史轉(zhuǎn)數(shù)。我們可能想知道每個(gè)輪胎的里程數(shù)和磨損度。要想知道哪個(gè)輪胎在哪兒,必須將輪胎標(biāo)識(shí)為Entity。輪胎被安在汽車上,也不會(huì)有人要系統(tǒng)中查詢特定的輪胎,然后看看這個(gè)輪胎在哪輛汽車上。人們只會(huì)在數(shù)據(jù)庫(kù)中查找汽車,然后臨時(shí)查看一下這部汽車的輪胎情況,因此,汽車聚合中的根Entity,而輪胎只是處于這個(gè)聚合的邊界之內(nèi)。

復(fù)制代碼

作為一名普通的手機(jī)用戶,當(dāng)屏幕摔碎的時(shí)候,他會(huì)選擇將整個(gè)手機(jī)送至維修中心。因?yàn)閷?duì)他來說手機(jī)是一個(gè)整體。會(huì)不會(huì)有人自己把屏幕單獨(dú)送去維修中心呢?有吧,可能他是維修師傅。

通過ID引用

復(fù)制代碼

public class Student {public int ID { get; set; }public string LastName { get; set; }public string FirstMidName { get; set; }public DateTime EnrollmentDate { get; set; }public ICollection<Enrollment> Enrollments { get; set; } }public class Enrollment {public int EnrollmentID { get; set; }public int CourseID { get; set; }public int StudentID { get; set; }public Grade? Grade { get; set; }public Course Course { get; set; }public Student Student { get; set; } }

該代碼摘自aspnetcore基礎(chǔ)教程

這樣的代碼是很常見的,許多開發(fā)人員都找到了一種自然方式在代碼中將關(guān)系建模為對(duì)象引用。特別是在使用EF Core中,我們會(huì)很自然的將不同對(duì)象之間的關(guān)系通過對(duì)象引用來表示。這是因?yàn)槲覀円酝]有聚合的這一概念,所以我們要完成一個(gè)關(guān)聯(lián)的操作就需要加載所有的關(guān)聯(lián)對(duì)象然后通過遍歷一個(gè)一個(gè)的實(shí)例對(duì)象來處理。很顯然,這會(huì)造成性能上的浪費(fèi),雖然我們可以使用延遲加載的技術(shù)來處理,但是延遲加載會(huì)讓模型的處理更加復(fù)雜。

在上面的例子中,假如我們需要知道這個(gè)行程創(chuàng)建的管理員用戶是誰。我們會(huì)怎么處理呢?管理員用戶被抽象為了一個(gè)單獨(dú)的聚合根User,該聚合包含了User所有的信息(身份,姓名,性別等等)。我們會(huì)在Itinerary聚合根中添加一個(gè)類型為User的屬性作為管理員嗎?不需要,在該領(lǐng)域中,我們?yōu)槭裁匆P(guān)心管理員的其他信息呢,以至于每次加載行程都還需要帶出用戶的信息。很顯然,我們?cè)?strong>Itinerary聚合中并不會(huì)關(guān)心另外聚合的情況。所以,當(dāng)一個(gè)聚合需要引用到另外一個(gè)聚合的時(shí)候,千萬不要直接使用類型的強(qiáng)引用方式來實(shí)現(xiàn),而是通過使用引用聚合的ID來維持聚合與聚合的關(guān)系

這樣做的好處在分布式系統(tǒng)中更容易體現(xiàn),旅程和用戶這兩者往往會(huì)被放在不同的系統(tǒng)中,旅程邊界中根本就找不到一個(gè)叫做User的實(shí)體,而它們之間的引用關(guān)系只能通過ID來標(biāo)記。

聚合真的是不變的嗎

可能我們通過分析領(lǐng)域模型,已經(jīng)建立了一個(gè)相對(duì)來說很好的聚合了,并且提取出了聚合根,將領(lǐng)域?qū)ο罂刂圃诰酆细膬?nèi)部。但是?聚合根里面的實(shí)體就永遠(yuǎn)存在聚合根之內(nèi)嗎?答案是不一定的。我們之所以將實(shí)體放置在聚合根之內(nèi)是因?yàn)槲覀冎浪c聚合根是一體的,外界訪問該實(shí)體的時(shí)候一定會(huì)攜帶上訪問聚合根實(shí)體。但是!!!!!假如我們需求的變更讓我們確確實(shí)實(shí)需要單獨(dú)訪問目前聚合根里面的實(shí)體呢??是的,它可能會(huì)被單獨(dú)提升為一個(gè)聚合根。而且通過ID之間的引用保持對(duì)原有聚合根之間的關(guān)聯(lián)關(guān)系。

所以考慮聚合根的重要一點(diǎn)是:在領(lǐng)域中我們是否會(huì)單獨(dú)訪問該實(shí)體

小的聚合

有時(shí)候,聚合的優(yōu)勢(shì)可能會(huì)成為糖衣炮彈,它會(huì)讓你瘋狂的將大量的實(shí)體和值對(duì)象融入在其中去,最后的結(jié)果是造成聚合越來越大。這樣會(huì)造成性能的瓶頸,特別是在某個(gè)實(shí)體存在大量結(jié)果的情況下,這簡(jiǎn)直是一個(gè)噩夢(mèng)。所以在考慮聚合之前,我們要多思考,我們是否將聚合設(shè)計(jì)的過大了。

哪怕在某個(gè)領(lǐng)域設(shè)計(jì)出來的聚合是正確的,我們有時(shí)候也會(huì)拆分它。原因很簡(jiǎn)單,性能問題。當(dāng)聚合A中的實(shí)體EntityA存在大量數(shù)據(jù)的時(shí)候,我們?cè)L問聚合A不得不去加載它們,這樣會(huì)讓性能造成大量損失。哪怕建模的結(jié)果是正確的,但是我們還是會(huì)考慮折中的辦法,將EntityA提升為一個(gè)單獨(dú)的聚合供外界單獨(dú)訪問。

一致性

聚合中的所有對(duì)象都應(yīng)該保持一致的變更,這是毫無疑問的。因此一個(gè)聚合在持久化的時(shí)候理應(yīng)在一個(gè)事務(wù)中完成。但是當(dāng)一個(gè)業(yè)務(wù)用例可能會(huì)操作多個(gè)聚合的時(shí)候,修改了聚合A的同時(shí)也更改了聚合B,這是一個(gè)很常見的操作,我們也必須保證多個(gè)聚合之間的一致性。這在單體應(yīng)用中很容易實(shí)現(xiàn),但是在分布式系統(tǒng)中我們不得不考慮最終一致性。有關(guān)分布式的相關(guān)信息將在后期?《分布式中的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》?系列中講述。

總結(jié)

本次我們介紹了有關(guān)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中“聚合”的內(nèi)容,我們知道了什么是聚合根,以及聚合根與實(shí)體之間的關(guān)系,以及怎么去考慮設(shè)計(jì)一個(gè)聚合根。在實(shí)際的項(xiàng)目中,其實(shí)聚合根是一個(gè)非常常見的領(lǐng)域?qū)ο?#xff0c;因?yàn)槲覀兇罅康臉I(yè)務(wù)邏輯和表達(dá)都是通過聚合根來完成操作的。回顧一下你現(xiàn)在正準(zhǔn)備嘗試或者已經(jīng)在寫的DDD項(xiàng)目,你使用聚合根了嗎?你又是怎么來表達(dá)聚合根的?

下一期的文章中,是關(guān)于倉(cāng)儲(chǔ)的,它與聚合根其實(shí)有密不可分的關(guān)系。

人生如夢(mèng),韶華白首,轉(zhuǎn)瞬即逝,因而生應(yīng)盡歡。

總結(jié)

以上是生活随笔為你收集整理的如何运用领域驱动设计 - 聚合的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。