自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

無法實(shí)施富領(lǐng)域模型的罪魁禍?zhǔn)渍业搅?/h1>

開發(fā) 前端
領(lǐng)域模型具有了行為以后,就成為了一個(gè)個(gè)動作靈活的舞蹈家,但很多情況下觀眾并不想只看某一個(gè)舞蹈家展示他們的動作,所以,應(yīng)用服務(wù)需要把一個(gè)或若干個(gè)領(lǐng)域模型的行為編排起來,來完成符合某個(gè)場景(Use Case)需要的一支舞蹈。

[[354879]]

本文轉(zhuǎn)載自微信公眾號「codeasy」,作者閻華  。轉(zhuǎn)載本文請聯(lián)系codeasy公眾號。  

 要弄清楚使用富領(lǐng)域模型有什么問題,我們要先從應(yīng)用服務(wù)層怎么使用富領(lǐng)域模型說起。

應(yīng)用服務(wù)是編舞者

領(lǐng)域模型具有了行為以后,就成為了一個(gè)個(gè)動作靈活的舞蹈家,但很多情況下觀眾并不想只看某一個(gè)舞蹈家展示他們的動作,所以,應(yīng)用服務(wù)需要把一個(gè)或若干個(gè)領(lǐng)域模型的行為編排起來,來完成符合某個(gè)場景(Use Case)需要的一支舞蹈。

我們來看看 SprintApplicationService 這個(gè)應(yīng)用服務(wù)里的一個(gè)方法:

  1. /** 
  2.  * 將一個(gè)BacklogItem提交到一個(gè)Sprint中 
  3.  * @param aCommand 表示客戶端發(fā)起的一個(gè)命令 
  4.  */ 
  5. public void commitBacklogItemToSprint( 
  6.             CommitBacklogItemToSprintCommand aCommand) { 
  7.  
  8.         TenantId tenantId = new TenantId(aCommand.getTenantId()); 
  9.  
  10.      //Step1:加載一個(gè)sprint到內(nèi)存 
  11.         Sprint sprint = 
  12.                 this.sprintRepository() 
  13.                     .sprintOfId( 
  14.                             tenantId, 
  15.                             new SprintId(aCommand.getSprintId())); 
  16.  
  17.      //Step2: 加載一個(gè)BacklogItem到內(nèi)存 
  18.         BacklogItem backlogItem = 
  19.                 this.backlogItemRepository() 
  20.                     .backlogItemOfId( 
  21.                             tenantId, 
  22.                             new BacklogItemId(aCommand.getBacklogItemId())); 
  23.  
  24.      //Step3:將BacklogItem提交到一個(gè)sprint,內(nèi)存級操作 
  25.         sprint.commit(backlogItem); 
  26.  
  27.      //Step4:持久化sprint 
  28.         this.sprintRepository().save(sprint); 
  29.  } 

這里Sprint和BacklogItem是兩個(gè)聚合根,他們分別對應(yīng)了一組實(shí)體。

第一步和第二步從數(shù)據(jù)庫加載了兩個(gè)聚合到內(nèi)存,在內(nèi)存里有兩個(gè)對象圖:

而當(dāng)我們執(zhí)行完第三步時(shí)(即執(zhí)行 sprint.commit(backlogItem) 后),內(nèi)存里的對象圖變成了:

這時(shí),在 sprint的backlogItems 這個(gè)集合里,多出一個(gè) cb3 ,它 的 ordering 是 3 , backlogItemId 是 12 。

當(dāng)把 id 是 12 的 backlogItem 加入到 spint 里,需要做一些校驗(yàn),以及新產(chǎn)生一個(gè) cb3 并正確設(shè)定它的 ordering 的值,這些都是 sprint 這個(gè)聚合內(nèi)部發(fā)生的邏輯,應(yīng)用服務(wù)是不知道這些領(lǐng)域邏輯的,甚至都不知道有這些邏輯的存在。

更復(fù)雜的場景,可能導(dǎo)致聚合內(nèi)多個(gè)對象的內(nèi)存狀態(tài)發(fā)生了變化。

注意,這時(shí)候只是內(nèi)存里對象的狀態(tài)發(fā)生了變化。到了第四步時(shí),應(yīng)用服務(wù)委托 sprintRepository 去持久化 sprint 后,內(nèi)存對象的變化才會反應(yīng)到對應(yīng)的數(shù)據(jù)庫的表(一個(gè)或多個(gè))內(nèi)容的變化(即更新或插入了數(shù)據(jù))—— 導(dǎo)致多少表的什么變化,應(yīng)用服務(wù)也是不知道的。

正是由于這樣職責(zé)劃分,才會出現(xiàn)我們第一篇文章里看到的結(jié)果 —— 領(lǐng)域?qū)拥拇a很豐富,而應(yīng)用層的代碼很少,只有這里看到的編排邏輯。這帶來的好處前兩篇文章說了很多了,不再贅述了。

但這樣做有什么問題呢?

為什么會投鼠忌器

我們說不敢使用富領(lǐng)域模型一定是有顧慮的,既然投鼠忌器,那這個(gè)“器”是什么呢?

可能有些人已經(jīng)看出來了,“器”有兩個(gè):

  • 性能
  • 并發(fā)沖突

如果我們只是按面向?qū)ο蟮脑O(shè)計(jì)方法去實(shí)現(xiàn)富領(lǐng)域模型,可能會導(dǎo)致對象關(guān)聯(lián)太多,內(nèi)存中的對象圖會是下面這個(gè)樣子:

image.png

連線表示對象引用

那在應(yīng)用服務(wù)層可能會加載非常多的對象到內(nèi)存里,很費(fèi)內(nèi)存。另外,修改時(shí)可能導(dǎo)致很多對象狀態(tài)的變更,修改引發(fā)的并發(fā)沖突會比較多。

DDD恰恰是要解決這個(gè)問題的,它推薦把對象分成不同的“小組”,也就是我們前面說的聚合。聚合和聚合之間是不能做對象引用的,只能用ID引用,這樣加載一個(gè)聚合時(shí)不會把其他聚合也加載到內(nèi)存。

image.png

黑色的鏈接線表示的是ID引用

總結(jié)一句話,要通過小的聚合來避免性能和修改的并發(fā)沖突問題。

但是……

聚合要多小才合適

但是聚合多小才算合適呢?極端情況下,一個(gè)表一個(gè)聚合就足夠小了,但這又回到了貧血模型。

聚合還是要代表一個(gè)業(yè)務(wù)一致性邊界的,比如OrderItem的屬性變化,和Order的屬性變化應(yīng)該保證一定的業(yè)務(wù)規(guī)則不被破壞,在這個(gè)前提下,聚合要設(shè)計(jì)的盡可能小。

從IDDD_Sample的代碼里,我們是看不到設(shè)計(jì)聚合的分析過程的,只能看到結(jié)果,想知道分析的過程,推薦去看《實(shí)現(xiàn)領(lǐng)域驅(qū)動設(shè)計(jì)》書中對這個(gè)例子的分析過程。

我們在后續(xù)的文章會分析另外一個(gè)開源示例(Library),那個(gè)例子里給出了分析過程的記錄,到時(shí)候再詳細(xì)講解聚合的識別過程。

在我實(shí)踐的過程中,發(fā)現(xiàn)大部分人設(shè)計(jì)的聚合都偏大。最近我嘗試使用領(lǐng)域故事會和事件風(fēng)暴這兩個(gè)方法來識別聚合,發(fā)現(xiàn)得到的聚合比以前的更小更合理。這得益于基于場景去分析,而不是從技術(shù)的角度去建模。

《領(lǐng)域驅(qū)動設(shè)計(jì)模式、原理與實(shí)踐》是另一本非常棒的關(guān)于DDD的書,里面曾經(jīng)說過“如果你發(fā)現(xiàn)一個(gè)聚合可能會帶來性能和并發(fā)的問題,就要回過頭去看看聚合是不是設(shè)計(jì)的太大了”。之前我一直覺得這是因果倒置的無奈之舉?,F(xiàn)在感覺是有合理的邏輯在的:

  • 按場景分析的話,聚合的粒度會比較小
  • 如果發(fā)現(xiàn)有性能和并發(fā)的問題,說明聚合太大了
  • 那可能是沒有按場景分析,所以要再按場景重新審視一下聚合的設(shè)計(jì)

所以這個(gè)技術(shù)問題本質(zhì)上是一個(gè)模型分析/設(shè)計(jì)的問題,但分析/設(shè)計(jì)的問題比技術(shù)問題更難解決,更難有固定的套路。后續(xù)我也打算寫另外一個(gè)系列,是關(guān)于DDD設(shè)計(jì)過程中的反模式的,其中就有很多是關(guān)于不合理的聚合設(shè)計(jì)的。

接下來聊聊CQRS

現(xiàn)在,我們還是聚焦在IDDD_Sample示例的代碼分析。聚合設(shè)計(jì)過大其中有一個(gè)原因,是開發(fā)人員考慮了太多的查詢的需要。合理地使用CQRS模式可以避免這個(gè)問題。另外,使用CQRS本身也能解決很多的性能問題。

我們下一篇看看IDDD_Sample中是怎么運(yùn)用CQRS這個(gè)模式的。

 

責(zé)任編輯:武曉燕 來源: codeasy
相關(guān)推薦

2020-10-26 16:35:53

內(nèi)存JavaThreadLocal

2019-05-27 10:22:26

Oracle日志數(shù)據(jù)庫

2011-04-21 16:34:56

打印亂碼接口

2019-06-04 14:19:53

AWS谷歌巖機(jī)

2015-11-23 10:29:48

app隱藏通信安卓耗電

2021-09-06 11:39:39

筆記本噪音風(fēng)扇

2009-02-25 08:58:30

裁員上網(wǎng)本微軟

2021-12-12 21:51:54

人工智能銀行內(nèi)卷

2015-10-14 11:32:55

機(jī)房空調(diào)制冷

2018-01-29 23:13:47

大數(shù)據(jù)戰(zhàn)略數(shù)據(jù)分析

2022-11-16 16:14:46

單踏板模式特斯拉

2010-07-12 16:24:20

2020-05-19 13:54:02

成熟度模型數(shù)據(jù)科學(xué)數(shù)據(jù)分析

2010-09-12 23:07:53

2009-10-12 19:44:40

Windows 7閃屏解決辦法

2009-07-27 13:56:10

2011-06-28 10:21:47

2011-08-12 10:04:52

數(shù)據(jù)中心宕機(jī)EPO

2018-09-10 09:43:26

2009-01-06 16:13:44

環(huán)路無線網(wǎng)絡(luò)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號