在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

DDD學(xué)習(xí)與感悟——向屎山?jīng)_鋒

京東云 ? 來(lái)源:jf_75140285 ? 作者:jf_75140285 ? 2024-09-24 13:31 ? 次閱讀

軟件系統(tǒng)是通過(guò)軟件開(kāi)發(fā)來(lái)解決某一個(gè)業(yè)務(wù)領(lǐng)域或問(wèn)題單元而產(chǎn)生的一個(gè)交付物。而通過(guò)軟件設(shè)計(jì)可以幫助我們開(kāi)發(fā)出更加健壯的軟件系統(tǒng)。因此,軟件設(shè)計(jì)是從業(yè)務(wù)領(lǐng)域到軟件開(kāi)發(fā)之間的橋梁。而DDD是軟件設(shè)計(jì)中的其中一種思想,旨在提供一種大型復(fù)雜軟件的設(shè)計(jì)思路和規(guī)范。通過(guò)DDD思想可以讓我們的業(yè)務(wù)架構(gòu)、系統(tǒng)架構(gòu)、部署架構(gòu)、數(shù)據(jù)架構(gòu)、工程架構(gòu)等都具備高擴(kuò)展性、高維護(hù)性和高測(cè)試性。

但是落地DDD是一件很困難的事情。首先在思想認(rèn)知層面就比較難以突破。

DDD本身是一種思想,不是某種具體的技術(shù),因此在代碼實(shí)現(xiàn)和系統(tǒng)架構(gòu)層面沒(méi)有約束。而由于市面上成熟的ORM框架(比如hibernate、mybatis等),使得大部分軟件開(kāi)發(fā)都是直接面向數(shù)據(jù)庫(kù)開(kāi)發(fā)。在傳統(tǒng)開(kāi)發(fā)中的應(yīng)用分層架構(gòu)又和DDD思想的分層架構(gòu)很類(lèi)似。從而導(dǎo)致很多人在初學(xué)DDD時(shí)有一定的理解偏差,從而導(dǎo)致無(wú)法落地DDD思想。

這篇文章記錄我對(duì)DDD的學(xué)習(xí)、感悟與項(xiàng)目工程代碼重構(gòu)實(shí)戰(zhàn)心得!

一、Domin Primitive

領(lǐng)域“元數(shù)據(jù)”的意思。主要是講解領(lǐng)域的基本準(zhǔn)則。這也是使用DDD思想的基本準(zhǔn)則。

1.1 隱性的概念顯性化

exp:電話(huà)號(hào)碼通常是由區(qū)號(hào)編碼+號(hào)碼組成。在實(shí)際的業(yè)務(wù)中會(huì)有很多需要電話(huà)號(hào)碼的業(yè)務(wù)。比如登錄認(rèn)證、導(dǎo)購(gòu)分銷(xiāo)等業(yè)務(wù);我們需要對(duì)電話(huà)號(hào)碼進(jìn)行基礎(chǔ)性校驗(yàn);獲取區(qū)號(hào)編碼等;在常規(guī)操作下,會(huì)在每一個(gè)用到電話(huà)號(hào)碼的方法入口都會(huì)寫(xiě)大量的這種校驗(yàn)代碼和判斷代碼,盡管我們可以將它的校驗(yàn)和獲取區(qū)號(hào)編碼抽離成util類(lèi)(實(shí)際上大多數(shù)工程中都是這么做的),但這種方式治標(biāo)不治本。基于DDD思想可以發(fā)現(xiàn)這里有一個(gè)隱性概念:區(qū)號(hào)編碼。

我們可以基于DDD思想,將電話(huà)號(hào)碼創(chuàng)建為一個(gè)擁有獨(dú)立概念和行為的值對(duì)象:PhoneNumber,將基礎(chǔ)性校驗(yàn)和獲取編碼等無(wú)狀態(tài)行為封裝在值對(duì)象中。這樣在方法中就不需要再充斥著寫(xiě)大量的校驗(yàn)和判斷。

1.2 隱性的上下文顯性化

exp:在銀行轉(zhuǎn)賬場(chǎng)景中,通常我們會(huì)說(shuō)A賬戶(hù)給B賬戶(hù)轉(zhuǎn)1000元。這里的1000元實(shí)際上有兩層含義,數(shù)字1000,貨幣元。但我們通常會(huì)忽略貨幣單位元。導(dǎo)致在實(shí)現(xiàn)轉(zhuǎn)賬功能時(shí),沒(méi)有考慮到單位。一旦有國(guó)際轉(zhuǎn)賬時(shí),就又會(huì)陷入到大量的if else中。

我們基于DDD思想,將錢(qián)創(chuàng)建為一個(gè)擁有獨(dú)立概念和行為的值對(duì)象:Money,這樣我們所說(shuō)的錢(qián)才具備完整的概念。通過(guò)這種方式就可以將貨幣這個(gè)隱性上下文顯性化,從而避免當(dāng)前未識(shí)別到但是未來(lái)可能會(huì)爆雷的bug。

1.3 封裝多對(duì)象行為

exp:在跨境轉(zhuǎn)賬的場(chǎng)景中,需要轉(zhuǎn)換匯率,我們可以將轉(zhuǎn)換匯率封裝成一個(gè)值對(duì)象。通過(guò)封裝金額計(jì)算邏輯和各種校驗(yàn)邏輯,使得整個(gè)方法極其簡(jiǎn)單。

1.4 DP和值對(duì)象的區(qū)別

DP是阿里大神提出來(lái)的概念;值對(duì)象是DDD思想中的概念。

學(xué)習(xí)之后,我個(gè)人認(rèn)為DP是對(duì)值對(duì)象的進(jìn)一步補(bǔ)充,使其擁有了更加完整的概念。在值對(duì)象【不變性】的基礎(chǔ)上補(bǔ)充了【可校驗(yàn)性】和【獨(dú)立行為】。當(dāng)然也是要求【無(wú)副作用】。所謂的無(wú)副作用就是【狀態(tài)不可變】。

1.5 DP和DTO的區(qū)別

DTO DP
功能 數(shù)據(jù)傳輸對(duì)象,屬于技術(shù)細(xì)節(jié) 屬于領(lǐng)域中的業(yè)務(wù)概念
數(shù)據(jù)關(guān)聯(lián)性 不具備數(shù)據(jù)關(guān)聯(lián)性 數(shù)據(jù)之間有強(qiáng)關(guān)聯(lián)性
行為 無(wú)行為 擁有十分豐富的行為和業(yè)務(wù)邏輯

1.6 使用DP VS 不使用DP

不使用DP 使用DP
API接口清晰度 含混不清 方法簽名清晰易懂
數(shù)據(jù)校驗(yàn)、錯(cuò)誤處理 校驗(yàn)邏輯分布多個(gè)地方、大量重復(fù)代碼 校驗(yàn)邏輯內(nèi)聚,在方法邊界外完成
業(yè)務(wù)代碼的清晰度 充斥大量膠水代碼,淹沒(méi)業(yè)務(wù)核心邏輯 代碼簡(jiǎn)潔明了,業(yè)務(wù)邏輯一目了然
測(cè)試復(fù)雜度 TC數(shù)量:NMP(N個(gè)參數(shù),每個(gè)參數(shù)M種校驗(yàn),有P個(gè)方法在調(diào)用) TC數(shù)量:N+M+P
其他好處 整體安全性大大提升、不可變性、線(xiàn)程安全

二、應(yīng)用架構(gòu)

2.1 DDD思想下的標(biāo)準(zhǔn)應(yīng)用架構(gòu)

傳統(tǒng)的MVC架構(gòu)分為展現(xiàn)層、業(yè)務(wù)邏輯層和數(shù)據(jù)訪(fǎng)問(wèn)層,更加注重從展現(xiàn)層到數(shù)據(jù)訪(fǎng)問(wèn)層自上而下的交互,編寫(xiě)出來(lái)的代碼像是腳本式代碼。

而基于DDD原則,工程架構(gòu)被分為應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)設(shè)施層。將工程中不同的功能和職責(zé)劃分到不同的層級(jí)中。核心的業(yè)務(wù)邏輯放在領(lǐng)域?qū)又小?/p>

wKgZombyTo2AZoglAAGro_B2Gyo735.png

2.1.1 應(yīng)用層

按照DDD的思想,應(yīng)用層負(fù)責(zé)協(xié)調(diào)用戶(hù)界面和領(lǐng)域?qū)又g的交互。可以通俗的認(rèn)為是對(duì)領(lǐng)域服務(wù)的編排,其本身不包含任何業(yè)務(wù)邏輯。

2.1.2 領(lǐng)域?qū)?/p>

領(lǐng)域?qū)迂?fù)責(zé)實(shí)現(xiàn)核心業(yè)務(wù)的邏輯和規(guī)則。按照DDD的思想,這一層包含實(shí)體模塊、值對(duì)象模塊、事件、領(lǐng)域服務(wù)。

2.1.3 基礎(chǔ)設(shè)施層

基礎(chǔ)設(shè)施層不處理任何業(yè)務(wù)邏輯,只包含基礎(chǔ)設(shè)施,通常包含數(shù)據(jù)庫(kù)、定時(shí)任務(wù)、MQ、南向網(wǎng)關(guān)、北向網(wǎng)關(guān)等。

2.2 我對(duì)演進(jìn)出六邊形架構(gòu)的理解

2.2.1 再談應(yīng)用層

在實(shí)際業(yè)務(wù)邏輯當(dāng)中,除了用戶(hù)界面層之外,還有其他外部系統(tǒng)會(huì)調(diào)用本服務(wù),比如xxljob、MQ、或者提供給外部系統(tǒng)調(diào)用http或者rpc接口等。因此在實(shí)際當(dāng)中,應(yīng)用層應(yīng)當(dāng)是協(xié)調(diào)外部系統(tǒng)與領(lǐng)域?qū)又g的交互。

按照標(biāo)準(zhǔn)架構(gòu)層級(jí)依賴(lài)關(guān)系來(lái)看,應(yīng)用層依賴(lài)了領(lǐng)域?qū)雍突A(chǔ)設(shè)施層。由于依賴(lài)了基礎(chǔ)設(shè)施層,因此破壞了應(yīng)用層本身的可維護(hù)性和測(cè)試性。因此我們需要基于接口進(jìn)行依賴(lài)倒置。

為了防止領(lǐng)域概念外泄,需要對(duì)應(yīng)用層進(jìn)一步的抽象為外部服務(wù)和內(nèi)部服務(wù),所有外部服務(wù)必須通過(guò)內(nèi)部服務(wù)調(diào)用領(lǐng)域?qū)印_@樣就可以防止領(lǐng)域模型的外泄。

2.2.2 再談?lì)I(lǐng)域?qū)?/p>

同樣的,按照標(biāo)準(zhǔn)架構(gòu)層級(jí)依賴(lài)關(guān)系,領(lǐng)域?qū)右蕾?lài)基礎(chǔ)設(shè)施層,但這也破壞了領(lǐng)域?qū)颖旧淼目删S護(hù)性和可測(cè)試性。因此我們基于DDD中的資源庫(kù)思想,抽象repository層,通過(guò)接口實(shí)現(xiàn)依賴(lài)反轉(zhuǎn)。讓領(lǐng)域?qū)硬辉僖蕾?lài)基礎(chǔ)設(shè)施層。從而提高領(lǐng)域?qū)颖旧淼目删S護(hù)性和可測(cè)試性。

2.2.3 再談基礎(chǔ)設(shè)施層

對(duì)于基礎(chǔ)設(shè)施層而言,它主要作用是提供基礎(chǔ)設(shè)施的能力,比如數(shù)據(jù)庫(kù)、MQ、遠(yuǎn)程服務(wù)調(diào)用等。進(jìn)一步抽象可以發(fā)現(xiàn)它們就是端口和適配器。通過(guò)端口實(shí)現(xiàn)與外部系統(tǒng)的交互,通過(guò)適配器完成數(shù)據(jù)和概念的轉(zhuǎn)換。

2.2.4 演進(jìn)出六邊形架構(gòu)

通過(guò)依賴(lài)反轉(zhuǎn),神奇的事情發(fā)生了。基礎(chǔ)設(shè)施層變成了最外層。

wKgZombyTpGAMlyZAACXPkEyKGA660.png

我們結(jié)合對(duì)應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)設(shè)施層進(jìn)一步的理解再加上反轉(zhuǎn)后的應(yīng)用架構(gòu),便可以得到六邊形架構(gòu):

wKgaombyTpKABMn8AAPtceC-3WQ829.png

2.3 工具類(lèi)、配置類(lèi)的代碼應(yīng)該放在哪里?

在一個(gè)實(shí)際的工程當(dāng)中,除了上面所說(shuō)的三層之外,通常會(huì)使用到一些工具類(lèi)(JSON解析工具類(lèi)、字符串工具類(lèi)等)。各層可能都會(huì)使用到工具類(lèi)。

從工具類(lèi)的定位來(lái)看,它應(yīng)當(dāng)屬于基礎(chǔ)設(shè)施層,但是基礎(chǔ)設(shè)施層屬于最上層,如果放在基礎(chǔ)設(shè)施層,那么就會(huì)破壞依賴(lài)順序。因此我們?cè)谶壿媱澐稚峡梢园压ぞ哳?lèi)歸類(lèi)為基礎(chǔ)設(shè)施或者通用域,在具體的工程結(jié)構(gòu)中,可以單獨(dú)一個(gè)模塊放工具類(lèi)。

在實(shí)際工程中還有一種類(lèi)型的代碼是配置相關(guān)的。從業(yè)務(wù)維度劃分的話(huà)可以分為業(yè)務(wù)類(lèi)配置和基礎(chǔ)設(shè)施類(lèi)配置。因此我們需要根據(jù)配置的類(lèi)型將其放在對(duì)應(yīng)的位置。比如為了靈活應(yīng)對(duì)業(yè)務(wù),我們通常會(huì)配置一個(gè)動(dòng)態(tài)開(kāi)關(guān),來(lái)動(dòng)態(tài)調(diào)整業(yè)務(wù)的邏輯,這種業(yè)務(wù)開(kāi)關(guān)類(lèi)的配置就應(yīng)該放在領(lǐng)域?qū)樱辉俦热鐢?shù)據(jù)庫(kù)的配置屬于基礎(chǔ)設(shè)施配置,這類(lèi)配置就應(yīng)當(dāng)放在基礎(chǔ)設(shè)施層。

2.4 我對(duì)于項(xiàng)目的六邊形架構(gòu)的實(shí)踐

我們團(tuán)隊(duì)做的的職責(zé)是業(yè)務(wù)底座,包含一系列的基礎(chǔ)能力建設(shè)。其中對(duì)于IDaaS系統(tǒng)而言,基于六邊形架構(gòu)實(shí)現(xiàn)出以下工程結(jié)構(gòu):

wKgZombyTpOAOQyBAAKqyzWq6tI241.png

三、repository模式

3.1 什么是repository模式?

在DDD思想中,repository表示資源庫(kù)的概念,用于區(qū)分?jǐn)?shù)據(jù)模型和領(lǐng)域模型。它操作的對(duì)象是聚合根,因此它屬于領(lǐng)域?qū)印?/p>

3.2 為什么要使用repository模式?

repository模式有兩個(gè)非常重要的作用:1、與底層存儲(chǔ)進(jìn)行解偶;2、為解決貧血模型提供了一種規(guī)范。

3.3 什么是貧血模型?

由于過(guò)去ER模型以及主流ORM框架的發(fā)展,讓很多開(kāi)發(fā)者對(duì)實(shí)體的概念還停留在與關(guān)系形數(shù)據(jù)庫(kù)映射這個(gè)層面。從而導(dǎo)致實(shí)體只有空洞的屬性,而實(shí)體的業(yè)務(wù)邏輯散落各個(gè)service、util、helper、handler等各種角落中。這種現(xiàn)象就被稱(chēng)為貧血模型現(xiàn)象。

如何判斷自己的工程是否有貧血模型現(xiàn)象?

1、大量的XxxDO或者Xxx:實(shí)體對(duì)象只包含與數(shù)據(jù)庫(kù)表映射的屬性,沒(méi)有行為或者及其少量的行為;

2、業(yè)務(wù)邏輯在各種service、controller、util、helper、handler中:實(shí)體的業(yè)務(wù)邏輯散落在不同層級(jí)、不同類(lèi)、不同方法中,相似場(chǎng)景有大量的重復(fù)代碼。

3.4 為什么貧血模型不好?

無(wú)法保證實(shí)體對(duì)象的完整性和一致性:貧血模型下,實(shí)體屬性的狀態(tài)和值只能由調(diào)用方保證,但是屬性的get和set是公開(kāi)的,因此所有調(diào)用方都可以調(diào)用。所以無(wú)法保證對(duì)象的完整性和一致性。

操作實(shí)體對(duì)象的邊界很難發(fā)現(xiàn):由于對(duì)象只有屬性,屬性的邊界值、調(diào)用范圍不受實(shí)體自身控制,各個(gè)地方都可以調(diào)用,邊界值和范圍也只能由調(diào)用方自行保障。如果實(shí)體的邊界值有所變化,那么所有調(diào)用方都需要調(diào)整,這種情況下很容易導(dǎo)致bug的產(chǎn)生。

強(qiáng)依賴(lài)底層:貧血模型下的實(shí)體和數(shù)據(jù)庫(kù)模型映射、協(xié)議等。因此如果底層改變,那么上層邏輯需要全部跟著改變。“軟件”變成了“固件”。

總結(jié)一句話(huà):貧血模型下,軟件的可維護(hù)性、可擴(kuò)展性、可測(cè)試性極差!

擴(kuò)展: 軟件的可維護(hù)性=底層基礎(chǔ)設(shè)施變化時(shí),需要新增/修改的代碼量是多少(越少可維護(hù)性越好) 軟件的可擴(kuò)展性=新增或變更業(yè)務(wù)邏輯時(shí),需要新增/修改的代碼量是多少(越少可擴(kuò)展性越好) 軟件的可測(cè)試性=每條TC執(zhí)行的時(shí)長(zhǎng) * 新增或變更業(yè)務(wù)邏輯時(shí)產(chǎn)生的TC(時(shí)長(zhǎng)越低/TC越少,測(cè)試性好)

3.5 實(shí)際情況中,為什么貧血模型難以消滅?

1、數(shù)據(jù)庫(kù)思維

隨著ER和ORM框架的發(fā)展,讓多數(shù)開(kāi)發(fā)者在剛?cè)腴T(mén)的時(shí)候(自學(xué)、培訓(xùn)等方式),就認(rèn)為實(shí)體就是數(shù)據(jù)庫(kù)表映射;從而簡(jiǎn)單的將面向業(yè)務(wù)領(lǐng)域開(kāi)發(fā)轉(zhuǎn)變成了面向數(shù)據(jù)庫(kù)開(kāi)發(fā),漸漸地就認(rèn)為軟件開(kāi)發(fā)就是CRUD。

2、簡(jiǎn)單

盡管有些架構(gòu)師或者開(kāi)發(fā)人員知道貧血模型不好,但是企業(yè)為了占領(lǐng)市場(chǎng),需要快速推出產(chǎn)品。因此工期被壓縮的很厲害。而貧血模型恰好簡(jiǎn)單,在軟件初期階段,可以快速實(shí)現(xiàn)業(yè)務(wù)邏輯。從而迫使開(kāi)發(fā)人員不得不“先實(shí)現(xiàn)了再說(shuō)”。這種現(xiàn)象也是行業(yè)的普遍現(xiàn)象。

3、腳本思維

有些開(kāi)發(fā)人員具備一定的抽象思維,將一些共性的代碼寫(xiě)成util、helper、handler等類(lèi)。但寫(xiě)代碼依然是腳本思維。比如一個(gè)方法中,先來(lái)個(gè)字段校驗(yàn)代碼,再來(lái)個(gè)對(duì)象轉(zhuǎn)換代碼,然后調(diào)用遠(yuǎn)程服務(wù),對(duì)遠(yuǎn)程服務(wù)返回的結(jié)果再來(lái)個(gè)對(duì)象轉(zhuǎn)換,……最后調(diào)用Dao類(lèi)的方法保存對(duì)象。這種代碼在很多工程中太常見(jiàn)了。

基于這些因素,導(dǎo)致貧血模型難以消滅。

這些因素的根本原因是什么?

根本原因就是,大部分的開(kāi)發(fā)人員混淆了數(shù)據(jù)模型和領(lǐng)域模型這兩個(gè)概念。

數(shù)據(jù)模型(Data Model):數(shù)據(jù)模型解決的是數(shù)據(jù)如何持久化、如何傳輸?shù)膯?wèn)題;

領(lǐng)域模型(Domin Model):領(lǐng)域指的是某一個(gè)獨(dú)立的業(yè)務(wù)領(lǐng)域或者問(wèn)題空間,領(lǐng)域模型就是解決這個(gè)業(yè)務(wù)領(lǐng)域或者問(wèn)題空間而設(shè)計(jì)的模型;解決的是業(yè)務(wù)領(lǐng)域的問(wèn)題。

在DDD中,repository就是用于區(qū)分?jǐn)?shù)據(jù)模型和領(lǐng)域模型提出來(lái)的概念。

3.6 使用repository之后,數(shù)據(jù)模型和領(lǐng)域模型如何轉(zhuǎn)換?

wKgaombyTpSASvFkAAKDd3-O8MY696.png

使用repository之后,數(shù)據(jù)模型和領(lǐng)域模型都各司其職。通過(guò)Assembler和Converter進(jìn)行模型之間的轉(zhuǎn)換。

在代碼中,動(dòng)態(tài)轉(zhuǎn)換映射 VS 靜態(tài)轉(zhuǎn)換映射

雖然Assembler/Converter是非常好用的對(duì)象,但是當(dāng)業(yè)務(wù)復(fù)雜時(shí),手寫(xiě)Assembler/Converter是一件耗時(shí)且容易出bug的事情,所以業(yè)界會(huì)有多種Bean Mapping的解決方案,從本質(zhì)上分為動(dòng)態(tài)和靜態(tài)映射。

動(dòng)態(tài)映射方案包括比較原始的 BeanUtils.copyProperties、能通過(guò)xml配置的Dozer等,其核心是在運(yùn)行時(shí)根據(jù)反射動(dòng)態(tài)賦值。動(dòng)態(tài)方案的缺陷在于大量的反射調(diào)用,性能比較差,內(nèi)存占用多,不適合特別高并發(fā)的應(yīng)用場(chǎng)景。而B(niǎo)eanUtils等copy類(lèi)工具隱藏了內(nèi)部copy的過(guò)程,很容易引發(fā)bug且不易排查。

MapStruct通過(guò)注解,在編譯時(shí)靜態(tài)生成映射代碼,其最終編譯出來(lái)的代碼和手寫(xiě)的代碼在性能上完全一致,且有強(qiáng)大的注解等能力。會(huì)節(jié)省大量的成本。

3.7 代碼層面模型規(guī)范和比較

DO Entity DTO
命名規(guī)范 XxxDO Xxx XxxDTO/XxxRequest/XxxVO/XxxCommand等
代碼層級(jí) 基礎(chǔ)設(shè)施層 領(lǐng)域?qū)?/td> 應(yīng)用層
字段名稱(chēng)標(biāo)準(zhǔn) 于數(shù)據(jù)庫(kù)字段保持一致 業(yè)務(wù)語(yǔ)言 和調(diào)用方商定
字段類(lèi)型標(biāo)準(zhǔn) 和數(shù)據(jù)庫(kù)字段保持一致 根據(jù)業(yè)務(wù)特征確定事基礎(chǔ)類(lèi)型還是值對(duì)象 和調(diào)用方商定
是否需要序列化 不需要 不需要 需要
轉(zhuǎn)換器 Assembler Assembler/Converter Converter

3.8 代碼層面repository規(guī)范

1、接口名命名規(guī)范

repository中的接口名不要使用底層存儲(chǔ)的名稱(chēng)(insert、update、add、delete、query等),而是盡量使用具有業(yè)務(wù)含義的命名。比如save、remove、find等。

2、接口的參數(shù)規(guī)范

repository操作的對(duì)象是聚合根。因此只能操作聚合根或者實(shí)體。這樣才能屏蔽底層的數(shù)據(jù)模型,避免數(shù)據(jù)模型滲透到領(lǐng)域?qū)印?/p>

四、領(lǐng)域?qū)釉O(shè)計(jì)規(guī)范

4.1 實(shí)體類(lèi)

大多數(shù)DDD架構(gòu)的核心都是實(shí)體類(lèi),實(shí)體類(lèi)包含了一個(gè)領(lǐng)域里的狀態(tài)、以及對(duì)狀態(tài)的直接操作。Entity最重要的設(shè)計(jì)原則是保證實(shí)體的不變性(Invariants),也就是說(shuō)要確保無(wú)論外部怎么操作,一個(gè)實(shí)體內(nèi)部的屬性都不能出現(xiàn)相互沖突,狀態(tài)不一致的情況。

4.1.1 創(chuàng)建即一致

constructor參數(shù)要包含所有必要屬性,或者在constructor里有合理的默認(rèn)值。

4.1.2 使用Factory模式來(lái)降低調(diào)用方復(fù)雜度

由于創(chuàng)建即一致的原則,導(dǎo)致實(shí)體的構(gòu)造方法可能會(huì)很復(fù)雜,因此可以使用Factory模式來(lái)快速的構(gòu)造出一個(gè)新的實(shí)體。降低調(diào)用方的復(fù)雜度。

4.1.3 盡量避免public setter

一個(gè)最容易導(dǎo)致不一致性的原因是實(shí)體暴露了public的setter方法,特別是set單一參數(shù)會(huì)導(dǎo)致?tīng)顟B(tài)不一致的情況。如果需要改變狀態(tài),盡量語(yǔ)義化方法名稱(chēng)。

4.1.4 通過(guò)聚合根保證主子實(shí)體的一致性

通常主實(shí)體會(huì)包含子實(shí)體,這時(shí)候主實(shí)體就需要起到聚合根的作用,即:

子實(shí)體不能單獨(dú)存在,只能通過(guò)聚合根的方法獲取到。任何外部的對(duì)象都不能直接保留子實(shí)體的引用

子實(shí)體沒(méi)有獨(dú)立的Repository,不可以單獨(dú)保存和取出,必須要通過(guò)聚合根的Repository實(shí)例化

子實(shí)體可以單獨(dú)修改自身狀態(tài),但是多個(gè)子實(shí)體之間的狀態(tài)一致性需要聚合根來(lái)保障

exp:常見(jiàn)的電商域中聚合的案例如主子訂單模型、商品/SKU模型、跨子訂單優(yōu)惠、跨店優(yōu)惠模型等。

4.1.5 不可以強(qiáng)依賴(lài)其他聚合根實(shí)體或領(lǐng)域服務(wù)

一個(gè)實(shí)體的原則是高內(nèi)聚、低耦合,即一個(gè)實(shí)體類(lèi)不能直接在內(nèi)部直接依賴(lài)一個(gè)外部的實(shí)體或服務(wù)。

對(duì)外部對(duì)象的依賴(lài)性會(huì)直接導(dǎo)致實(shí)體無(wú)法被單測(cè); 以及一個(gè)實(shí)體無(wú)法保證外部實(shí)體變更后不會(huì)影響本實(shí)體的一致性和正確性。

正確依賴(lài)外部的方式

只保存外部實(shí)體的ID:這里我再次強(qiáng)烈建議使用強(qiáng)類(lèi)型的ID對(duì)象,而不是Long型ID。強(qiáng)類(lèi)型的ID對(duì)象不單單能自我包含驗(yàn)證代碼,保證ID值的正確性,同時(shí)還能確保各種入?yún)⒉粫?huì)因?yàn)閰?shù)順序變化而出bug。

針對(duì)于“無(wú)副作用”的外部依賴(lài),通過(guò)方法入?yún)⒌姆绞絺魅搿1热缟衔闹械膃quip(Weapon,EquipmentService)方法。

4.1.6 任何實(shí)體的行為只能直接影響到本實(shí)體(和其子實(shí)體)

這個(gè)原則更多是一個(gè)確保代碼可讀性、可理解的原則,即任何實(shí)體的行為不能有“直接”的”副作用“,即直接修改其他的實(shí)體類(lèi)。這么做的好處是代碼讀下來(lái)不會(huì)產(chǎn)生意外。

另一個(gè)遵守的原因是可以降低未知的變更的風(fēng)險(xiǎn)。在一個(gè)系統(tǒng)里一個(gè)實(shí)體對(duì)象的所有變更操作應(yīng)該都是預(yù)期內(nèi)的,如果一個(gè)實(shí)體能隨意被外部直接修改的話(huà),會(huì)增加代碼bug的風(fēng)險(xiǎn)。

4.1.7 可以利用enum來(lái)代替繼承關(guān)系,后續(xù)也可以利用Type Object設(shè)計(jì)模式來(lái)做到數(shù)據(jù)驅(qū)動(dòng)

4.2 領(lǐng)域服務(wù)

當(dāng)一個(gè)業(yè)務(wù)邏輯需要用到多個(gè)領(lǐng)域?qū)ο笞鳛檩斎耄敵鼋Y(jié)果是一個(gè)值對(duì)象時(shí),就說(shuō)明需要使用到領(lǐng)域服務(wù)。

4.2.1 單對(duì)象策略型

這種領(lǐng)域?qū)ο笾饕嫦虻氖菃蝹€(gè)實(shí)體對(duì)象的變更,但涉及到多個(gè)領(lǐng)域?qū)ο蠡蛲獠恳蕾?lài)的一些規(guī)則。

在這種類(lèi)型下,實(shí)體應(yīng)該通過(guò)方法入?yún)⒌姆绞絺魅脒@種領(lǐng)域服務(wù),然后通過(guò)Double Dispatch來(lái)反轉(zhuǎn)調(diào)用領(lǐng)域服務(wù)的方法。

什么是Double Dispatch

exp:對(duì)于“玩家”實(shí)體而言,有一個(gè)“equip()”裝備武器的方法。 按照常規(guī)思路,“玩家”實(shí)體需要注入一個(gè)EquipmentService,然而實(shí)體只能保留自己的狀態(tài), 除此之外的其他對(duì)象實(shí)體無(wú)法保證其完整性,因此我們不通過(guò)注入的方式使用EquipmentService; 而是通過(guò)方法參數(shù)引入的方式來(lái)使用。即“玩家”實(shí)體的"equip()"方法定義為: public void equip(Weapon weapon, EquipmentService equipmentService) { if(equipmentService.canEquip(this, weapon)) { this.weaponId = weapon.getId(); } } 這種方式就稱(chēng)為Double Dispatch方式。 Double Dispatch是一個(gè)使用Domain Service經(jīng)常會(huì)用到的方法,類(lèi)似于調(diào)用反轉(zhuǎn)。

4.2.2 跨對(duì)象事務(wù)型

當(dāng)一個(gè)行為會(huì)直接修改多個(gè)實(shí)體時(shí),不能再通過(guò)單一實(shí)體的方法作處理,而必須直接使用領(lǐng)域服務(wù)的方法來(lái)做操作。在這里,領(lǐng)域服務(wù)更多的起到了跨對(duì)象事務(wù)的作用,確保多個(gè)實(shí)體的變更之間是有一致性的。

4.2.3 通用組件型

這種類(lèi)型的領(lǐng)域服務(wù)提供了組件化的行為,但本身又不直接綁死在一種實(shí)體類(lèi)上。他的好處是可以通過(guò)組件化服務(wù)降低代碼的重復(fù)性。

接口組件化來(lái)實(shí)現(xiàn)通用領(lǐng)域服務(wù)

exp:在游戲系統(tǒng)中,原價(jià)、NPC、怪物都是可移動(dòng)的。因此可以設(shè)計(jì)一個(gè)Movable接口, 讓玩家、NPC、怪物實(shí)體實(shí)現(xiàn)Movable接口。然后再實(shí)現(xiàn)一個(gè)MoveService,從而實(shí)現(xiàn)一個(gè)移動(dòng)通用服務(wù)。

4.3 策略對(duì)象(Domain Policy)

Policy或者Strategy設(shè)計(jì)模式是一個(gè)通用的設(shè)計(jì)模式,但是在DDD架構(gòu)中會(huì)經(jīng)常出現(xiàn),其核心就是封裝領(lǐng)域規(guī)則。

一個(gè)Policy是一個(gè)無(wú)狀態(tài)的單例對(duì)象,通常需要至少2個(gè)方法:canApply 和 一個(gè)業(yè)務(wù)方法。

canApply方法用來(lái)判斷一個(gè)Policy是否適用于當(dāng)前的上下文,如果適用則調(diào)用方會(huì)去觸發(fā)業(yè)務(wù)方法。 通常,為了降低一個(gè)Policy的可測(cè)試性和復(fù)雜度,Policy不應(yīng)該直接操作對(duì)象,而是通過(guò)返回計(jì)算后的值, 在Domain Service里對(duì)對(duì)象進(jìn)行操作。

4.4 副作用的處理方法 - 領(lǐng)域事件

什么是副作用?

“副作用”也是一種領(lǐng)域規(guī)則。一般的副作用發(fā)生在核心領(lǐng)域模型狀態(tài)變更后,同步或者異步對(duì)另一個(gè)對(duì)象的影響或行為。比如:當(dāng)用于積分達(dá)到100時(shí),會(huì)員等級(jí)升1級(jí)。

在DDD中,解決“副作用”的手段是領(lǐng)域事件。通過(guò)EventBus事件總線(xiàn)可以實(shí)現(xiàn)領(lǐng)域事件的傳播。

目前領(lǐng)域事件的缺陷和展望

由于實(shí)體需要保證完整性,因此不能夠直接依賴(lài)EventBus,所以EventBus只能保持全局singleton。但是全局singleton對(duì)象很難被單測(cè),這就容易導(dǎo)致Entity對(duì)象很難被完整單測(cè)覆蓋全。

五、寫(xiě)在最后

通過(guò)對(duì)于DDD的學(xué)習(xí)與實(shí)踐,越來(lái)越能夠體會(huì)到它作為一種軟件設(shè)計(jì)思想和指導(dǎo),對(duì)于大型復(fù)雜軟件的建設(shè)十分有幫助。對(duì)于歷史遺留屎山工程的重構(gòu)也提供了一個(gè)很好的指導(dǎo)方向。

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 框架
    +關(guān)注

    關(guān)注

    0

    文章

    404

    瀏覽量

    17739
  • ddd
    ddd
    +關(guān)注

    關(guān)注

    0

    文章

    23

    瀏覽量

    3006
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    澤HDMI 2.1:開(kāi)啟高清視界的新篇章

    在當(dāng)今這個(gè)視覺(jué)體驗(yàn)至上的時(shí)代,無(wú)論是家庭影院的構(gòu)建、游戲愛(ài)好者的追求,還是專(zhuān)業(yè)視頻編輯的需求,都離不開(kāi)高質(zhì)量的音視頻傳輸。而澤HDMI 2.1線(xiàn)纜,作為最新一代高性能傳輸解決方案,正是為滿(mǎn)足這些
    的頭像 發(fā)表于 03-13 18:06 ?237次閱讀

    澤HDMI 2.1:探索未來(lái)視聽(tīng)技術(shù)的前沿

    在數(shù)字娛樂(lè)迅速發(fā)展的今天,高清視頻和音頻傳輸已經(jīng)成為家庭影院、游戲體驗(yàn)以及專(zhuān)業(yè)演示中不可或缺的一部分。為了滿(mǎn)足用戶(hù)對(duì)極致視聽(tīng)體驗(yàn)的需求,澤(SAMZHE)推出了支持最新HDMI 2.1標(biāo)準(zhǔn)的線(xiàn)材
    的頭像 發(fā)表于 03-03 15:02 ?239次閱讀

    耐用且高效:澤HDMI線(xiàn)的性?xún)r(jià)比之選

    澤HDMI線(xiàn)在性?xún)r(jià)比方面一直享有良好的聲譽(yù),這得益于其提供的高質(zhì)量產(chǎn)品和相對(duì)親民的價(jià)格。以下是關(guān)于“澤HDMI線(xiàn)的性?xún)r(jià)比之選”的詳細(xì)介紹: 高質(zhì)量材料與設(shè)計(jì) 澤HDMI線(xiàn)采用高純度無(wú)氧銅芯
    的頭像 發(fā)表于 02-18 17:10 ?265次閱讀

    澤HDMI線(xiàn):耐用設(shè)計(jì),持久使用

    在當(dāng)今的數(shù)字化時(shí)代,高質(zhì)量的連接線(xiàn)對(duì)于確保設(shè)備之間的無(wú)縫通信至關(guān)重要。澤HDMI線(xiàn)以其卓越的設(shè)計(jì)和出色的耐用性脫穎而出,成為家庭娛樂(lè)系統(tǒng)、辦公室會(huì)議以及各類(lèi)多媒體應(yīng)用的理想選擇。 耐用設(shè)計(jì)
    的頭像 發(fā)表于 02-08 14:48 ?215次閱讀

    DEKRA德凱與中集洋、青蛙新能源簽署戰(zhàn)略合作協(xié)議

    近日,全球領(lǐng)先的檢驗(yàn)檢測(cè)認(rèn)證機(jī)構(gòu)DEKRA德凱分別與上海中集洋物流裝備有限公司(以下簡(jiǎn)稱(chēng)“中集洋”)以及蘇州青蛙新能源科技有限公司(以下簡(jiǎn)稱(chēng)“青蛙新能源”)達(dá)成戰(zhàn)略合作協(xié)議。簽約儀式在上海臨港新片區(qū)中集洋總部隆重舉行,標(biāo)志
    的頭像 發(fā)表于 01-23 13:45 ?444次閱讀

    為什么選擇澤HDMI線(xiàn)?

    無(wú)損音質(zhì)傳輸 澤HDMI ARC/eARC線(xiàn)支持高保真音頻傳輸,能夠傳輸Dolby Atmos、DTS:X等高級(jí)音效格式,保證音質(zhì)清晰、細(xì)膩。無(wú)論是看電影、聽(tīng)音樂(lè)還是玩游戲,都能享受身臨其境的音效
    的頭像 發(fā)表于 01-21 15:28 ?298次閱讀

    澤HDMI線(xiàn)質(zhì)量怎么樣?用戶(hù)評(píng)價(jià)揭曉秘密!

    在選擇HDMI線(xiàn)時(shí),大家最關(guān)心的無(wú)非是信號(hào)穩(wěn)定性、傳輸速率和使用壽命。今天我們就來(lái)看看澤HDMI線(xiàn)的真實(shí)用戶(hù)評(píng)價(jià),揭開(kāi)它在市場(chǎng)中的表現(xiàn)秘密。 1?? 高質(zhì)量的信號(hào)傳輸,用戶(hù)稱(chēng)贊無(wú)損畫(huà)質(zhì) 許多
    的頭像 發(fā)表于 01-13 15:28 ?670次閱讀

    澤HDMI 2.1:高清無(wú)損,品質(zhì)之選

    在這個(gè)視覺(jué)與聽(tīng)覺(jué)體驗(yàn)至上的時(shí)代,影音設(shè)備的連接線(xiàn)材早已不再是簡(jiǎn)單的配件,而是決定整體體驗(yàn)的關(guān)鍵。澤HDMI 2.1以其卓越的性能和匠心品質(zhì),成為追求極致影音體驗(yàn)用戶(hù)的理想之選。無(wú)論是家庭影院、游戲
    的頭像 發(fā)表于 12-31 14:35 ?333次閱讀

    澤HDMI 2.1 線(xiàn)材:連接未來(lái)娛樂(lè)的可靠選擇

    澤(SAMZHE)作為一家專(zhuān)注于高品質(zhì)數(shù)碼配件的品牌,其 HDMI 2.1 線(xiàn)材憑借出色的性能和可靠性贏得了市場(chǎng)的廣泛認(rèn)可。以下是關(guān)于澤 HDMI 2.1 線(xiàn)材的一些關(guān)鍵特點(diǎn)和優(yōu)勢(shì),幫助您了解
    的頭像 發(fā)表于 12-25 17:22 ?365次閱讀

    HDMI2.0與HDMI2.1如何選擇合適的線(xiàn)材?澤品牌分享

    HDMI2.0與HDMI2.1如何選擇合適的線(xiàn)材?澤品牌分享
    的頭像 發(fā)表于 11-30 16:02 ?758次閱讀

    澤HDMI 2.1線(xiàn)材:連接未來(lái)的高清視界

    在數(shù)字娛樂(lè)和高清視頻日益普及的今天,高質(zhì)量的HDMI線(xiàn)材成為了連接各種設(shè)備不可或缺的一部分。澤作為一家專(zhuān)注于高品質(zhì)線(xiàn)材制造的品牌,推出了其HDMI 2.1線(xiàn)材,旨在為用戶(hù)提供卓越的傳輸性能和可靠
    的頭像 發(fā)表于 11-26 16:14 ?482次閱讀

    海辰儲(chǔ)能柬埔寨三所小學(xué)捐贈(zèng)HeroEE光儲(chǔ)系統(tǒng)

    近日,海辰儲(chǔ)能攜手愛(ài)德基金會(huì),柬埔寨菩薩省豆蔻市的三所小學(xué)捐贈(zèng)了18套能源平權(quán)英雄HeroEE光儲(chǔ)系統(tǒng),旨在解決這些學(xué)校長(zhǎng)期面臨的電力短缺問(wèn)題,為孩子們創(chuàng)造一個(gè)更加光明和有利的學(xué)習(xí)環(huán)境。
    的頭像 發(fā)表于 11-21 09:34 ?477次閱讀

    優(yōu)質(zhì)HDMI線(xiàn)推薦:打造完美視聽(tīng)體驗(yàn) —— 澤(SAMZHE)

    隨著高清視頻技術(shù)和家庭娛樂(lè)系統(tǒng)的不斷進(jìn)步,選擇一條高質(zhì)量的HDMI線(xiàn)對(duì)于享受流暢、無(wú)損的視聽(tīng)體驗(yàn)至關(guān)重要。澤(SAMZHE)作為市場(chǎng)上備受推崇的品牌之一,以其高性能、高性?xún)r(jià)比的產(chǎn)品贏得了眾多消費(fèi)者的喜愛(ài)。本文將詳細(xì)介紹澤HDMI線(xiàn)的特點(diǎn)及為何它能夠成為打造完美視聽(tīng)體驗(yàn)
    的頭像 發(fā)表于 10-27 09:35 ?801次閱讀

    在DVEVM上通過(guò)ddd運(yùn)行Demo

    電子發(fā)燒友網(wǎng)站提供《在DVEVM上通過(guò)ddd運(yùn)行Demo.pdf》資料免費(fèi)下載
    發(fā)表于 10-15 10:05 ?0次下載
    在DVEVM上通過(guò)<b class='flag-5'>ddd</b>運(yùn)行Demo

    原生鴻蒙:我面對(duì),我就是路

    如今在腳下,路在眼前,開(kāi)發(fā)者行走其間
    的頭像 發(fā)表于 06-23 09:49 ?1762次閱讀
    原生鴻蒙:我面對(duì)<b class='flag-5'>山</b>,我就是路
    主站蜘蛛池模板: 5g影院天天| 操爽视频 | 天堂在线链接 | 色综合成人丁香 | 7777sq国产精品 | 色男人的天堂 | 色综合久久网 | 桃花色综合影院 | 综合色久七七综合七七蜜芽 | 欧美亚洲综合一区 | 欧美一级特黄乱妇高清视频 | 国产卡一卡2卡三卡免费视频 | 色噜噜狠狠色综合欧洲 | 4虎影视国产在线观看精品 4虎影院永久地址www | 亚洲一区有码 | 欧美特黄一级视频 | 久久男人的天堂色偷偷 | 久草视频资源在线 | 狠狠色丁香久久婷婷综 | 欧美日操| 亚洲国产精品第一区二区 | 伊人狼人在线 | 国产农村妇女毛片精品久久久 | 国产精品久久久久久一级毛片 | 中文在线免费看影视 | 欧美性69| 色噜噜色偷偷 | 一区二区三区亚洲 | 奇米影视7777久久精品 | 久久天天躁夜夜躁狠狠躁2015 | 一区二区三区高清不卡 | 美女鲜嫩bbbb | 欧亚色视频 | 99精品热视频 | 色香欲亚洲天天综合网 | 男人扒开美女尿口无遮挡图片 | 免费人成网555www | 1024你懂的国产日韩欧美 | 免费看黄视频的网站 | 欧美色碰碰碰免费观看长视频 | 国产精品丝袜 |