DDD是什么
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(Domain Driven Design,DDD)是由 Eric Evans 最早提出的綜合軟件系統(tǒng)分析和設(shè)計(jì)的面向?qū)ο蠼7椒?,如今已?jīng)發(fā)展成為了一種針對(duì)大型復(fù)雜系統(tǒng)的領(lǐng)域建模與分析方法。它完全改變了傳統(tǒng)軟件開發(fā)工程師針對(duì)數(shù)據(jù)庫(kù)進(jìn)行的建模方法,從而將要解決的業(yè)務(wù)概念和業(yè)務(wù)規(guī)則轉(zhuǎn)換為軟件系統(tǒng)中的類型以及類型的屬性與行為,通過(guò)合理運(yùn)用面向?qū)ο蟮姆庋b、繼承和多態(tài)等設(shè)計(jì)要素,降低或隱藏整個(gè)系統(tǒng)的業(yè)務(wù)復(fù)雜性,并使得系統(tǒng)具有更好的擴(kuò)展性,應(yīng)對(duì)紛繁多變的現(xiàn)實(shí)業(yè)務(wù)問(wèn)題。
- 本質(zhì):一種系統(tǒng)分析和設(shè)計(jì)方法
- 區(qū)別:不同于針對(duì)數(shù)據(jù)庫(kù)的建模方法,是面向領(lǐng)域?qū)嶓w
- 目標(biāo):用于降低系統(tǒng)復(fù)雜性
- 方法:將「業(yè)務(wù)概念」和「業(yè)務(wù)規(guī)則」轉(zhuǎn)換為系統(tǒng)中的「類型」以及類型的「屬性」與「行為」
- 核心:以領(lǐng)域?yàn)楹诵尿?qū)動(dòng)力,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)關(guān)注的焦點(diǎn)在于領(lǐng)域和領(lǐng)域邏輯,因?yàn)檐浖到y(tǒng)的本質(zhì)其實(shí)是給客戶(用戶)提供具有業(yè)務(wù)價(jià)值的領(lǐng)域功能。
數(shù)據(jù)驅(qū)動(dòng)設(shè)計(jì)VS服務(wù)驅(qū)動(dòng)設(shè)計(jì)VS領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)
數(shù)據(jù)驅(qū)動(dòng)設(shè)計(jì)關(guān)注的是數(shù)據(jù)表以及數(shù)據(jù)表之間關(guān)系的設(shè)計(jì),是典型的面向技術(shù)實(shí)現(xiàn)的建模方法,面對(duì)日漸復(fù)雜的業(yè)務(wù)邏輯,這種設(shè)計(jì)方法欠缺靈活性與可擴(kuò)展性,也無(wú)法更好地利用面向?qū)ο笤O(shè)計(jì)思想及設(shè)計(jì)模式,建立可重用的、可擴(kuò)展的代碼單元。
當(dāng)我們從服務(wù)視角建立服務(wù)模型時(shí),有兩種不同的設(shè)計(jì)思想。一種思想是將服務(wù)視為一種資源,即 REST (REpresentational State Transfer,表述性狀態(tài)遷移)架構(gòu)風(fēng)格的設(shè)計(jì)模式。采用這種設(shè)計(jì)思想建立的服務(wù)分析模型可以稱之為“服務(wù)資源模型”。
領(lǐng)域模型驅(qū)動(dòng)設(shè)計(jì)自然是以提煉和轉(zhuǎn)換業(yè)務(wù)需求中的領(lǐng)域知識(shí)為設(shè)計(jì)的起點(diǎn)。在提煉領(lǐng)域知識(shí)時(shí),沒有數(shù)據(jù)庫(kù)的概念,亦沒有服務(wù)的概念,一切圍繞著業(yè)務(wù)需求而來(lái)。尤其是領(lǐng)域建模的分析階段,應(yīng)該只關(guān)注問(wèn)題域,模型表達(dá)的是業(yè)務(wù)領(lǐng)域的概念,而非實(shí)現(xiàn)的概念。
| 出發(fā)點(diǎn) | 關(guān)注點(diǎn) | 模型 | 所處層 | 技術(shù)方案 | 應(yīng)用場(chǎng)景 | |
|---|---|---|---|---|---|---|
| 數(shù)據(jù)驅(qū)動(dòng)設(shè)計(jì) | 從數(shù)據(jù)存儲(chǔ)方案出發(fā) | 怎么存儲(chǔ) | 實(shí)體關(guān)系建模,貧血模型,只有數(shù)據(jù) | 基礎(chǔ)設(shè)施層 | 根據(jù)存儲(chǔ)方案密關(guān)聯(lián) | 簡(jiǎn)單業(yè)務(wù)場(chǎng)景,CRUD |
| 服務(wù)驅(qū)動(dòng)設(shè)計(jì) | 從服務(wù)提供的資源和行為能力出發(fā) | 提供什么服務(wù) | 接口對(duì)象建模,貧血模型,只有數(shù)據(jù) | 應(yīng)用層 | 不關(guān)注實(shí)現(xiàn)方案 | 簡(jiǎn)單業(yè)務(wù)場(chǎng)景,API服務(wù) |
| 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì) | 一切圍繞著領(lǐng)域知識(shí)進(jìn)行建模 | 領(lǐng)域劃分和建模 | 領(lǐng)域?qū)ο蠼?,充血模型,包括?shù)據(jù)和行為 | 領(lǐng)域?qū)?/td> | 不考慮存儲(chǔ)方式(技術(shù)實(shí)現(xiàn)) | 復(fù)雜業(yè)務(wù)場(chǎng)景 |
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)貫穿了整個(gè)軟件開發(fā)的生命周期,包括對(duì)需求的分析、建模、架構(gòu)、設(shè)計(jì),甚至最終的編碼實(shí)現(xiàn),乃至對(duì)編碼的測(cè)試與重構(gòu)。分戰(zhàn)略設(shè)計(jì)和戰(zhàn)術(shù)落地兩大部分:戰(zhàn)略設(shè)計(jì)強(qiáng)調(diào)系統(tǒng)層面的架構(gòu)模式,包括限界上下文、上下文映射、分層架構(gòu)等,可以運(yùn)用這些模式對(duì)整個(gè)系統(tǒng)的領(lǐng)域進(jìn)行“分而治之”,從而降低業(yè)務(wù)復(fù)雜度,同時(shí)圍繞“領(lǐng)域”為核心,建立業(yè)務(wù)復(fù)雜度與技術(shù)復(fù)雜度的邊界;戰(zhàn)術(shù)設(shè)計(jì)關(guān)注領(lǐng)域?qū)用娴脑O(shè)計(jì)模式,以“模型驅(qū)動(dòng)設(shè)計(jì)”為主線,貫穿分析、設(shè)計(jì)與編碼實(shí)現(xiàn)這三個(gè)不同的建?;顒?dòng),并引入領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的戰(zhàn)術(shù)設(shè)計(jì)要素,如實(shí)體、值對(duì)象、領(lǐng)域服務(wù)、領(lǐng)域事件、聚合、資源庫(kù)、工廠等。見下圖:
DDD的思想總結(jié)
DDD是一種方法論,是“分治”思想的更詳細(xì)描述,將復(fù)雜系統(tǒng)拆分成一個(gè)個(gè)盡可能垂直正交的小問(wèn)題域,讓每個(gè)問(wèn)題域完整自己的職責(zé),從而實(shí)現(xiàn)整體架構(gòu)的整潔清晰。DDD本質(zhì)還是遵循“高內(nèi)聚低耦合”的原則,跟SOLID原則是遙相呼應(yīng)的。DDD的戰(zhàn)略設(shè)計(jì)站在一個(gè)更高更抽象的角度拆分問(wèn)題域,戰(zhàn)術(shù)設(shè)計(jì)原則則指導(dǎo)具體的落地實(shí)現(xiàn)細(xì)節(jié)。
戰(zhàn)略設(shè)計(jì)
消化知識(shí)
做好一塊業(yè)務(wù)的第一步是如何消化一個(gè)知識(shí),消化一個(gè)知識(shí)其實(shí)就是建立有效模型,將知識(shí)透明化,變成可視化、可交流的文檔或者圖形。
在傳統(tǒng)的瀑布方法中,業(yè)務(wù)專家與分析員進(jìn)行討論,分析員消化理解這些知識(shí)后,對(duì)其進(jìn)行抽象并將結(jié)果結(jié)果傳遞給程序員,再由程序員編寫軟件代碼。由于這種方法沒有反饋,因此總是失敗。分析員全權(quán)負(fù)責(zé)創(chuàng)建模型,但他們創(chuàng)建的模型只是基于業(yè)務(wù)專家的意見。他們既沒有向程序員學(xué)習(xí)的機(jī)會(huì),也得不到早期軟件版本的經(jīng)驗(yàn)。知識(shí)只是朝一個(gè)方向在流動(dòng),但是不會(huì)積累。
好的程序員會(huì)自然而然地抽象并開發(fā)出一個(gè)可以完成更多工作的模型。但是這個(gè)模型只是局部的,只能滿足特定時(shí)刻特定部分的需求。只有團(tuán)隊(duì)所有成員一起消化理解模型,通過(guò)頻繁的交互才能形成全局有效一致的認(rèn)知。領(lǐng)域模型的不斷精化迫使開發(fā)人員學(xué)習(xí)重要的業(yè)務(wù)原理,而不是機(jī)械地進(jìn)行功能開發(fā)。
統(tǒng)一團(tuán)隊(duì)的領(lǐng)域交流語(yǔ)言
統(tǒng)一語(yǔ)言是提煉領(lǐng)域知識(shí)的產(chǎn)出物,獲得統(tǒng)一語(yǔ)言就是需求分析的過(guò)程,也是團(tuán)隊(duì)中各個(gè)角色就系統(tǒng)目標(biāo)、范圍與具體功能達(dá)成一致的過(guò)程。
使用統(tǒng)一語(yǔ)言可以幫助我們將參與討論的客戶、領(lǐng)域?qū)<遗c開發(fā)團(tuán)隊(duì)拉到同一個(gè)維度空間進(jìn)行討論,若沒有達(dá)成這種一致性,那就是雞同鴨講,毫無(wú)溝通效率,相反還可能造成誤解。因此,在溝通需求時(shí),團(tuán)隊(duì)中的每個(gè)人都應(yīng)使用統(tǒng)一語(yǔ)言進(jìn)行交流。
一旦確定了統(tǒng)一語(yǔ)言,無(wú)論是與領(lǐng)域?qū)<业挠懻?,還是最終的實(shí)現(xiàn)代碼,都可以通過(guò)使用相同的術(shù)語(yǔ),清晰準(zhǔn)確地定義領(lǐng)域知識(shí)。重要的是,當(dāng)我們建立了符合整個(gè)團(tuán)隊(duì)皆認(rèn)同的一套統(tǒng)一語(yǔ)言后,就可以在此基礎(chǔ)上尋找正確的領(lǐng)域概念,為建立領(lǐng)域模型提供重要參考。
統(tǒng)一語(yǔ)言體現(xiàn)在兩個(gè)方面:
- 統(tǒng)一的領(lǐng)域術(shù)語(yǔ)
- 領(lǐng)域?qū)<遥I(yè)務(wù)),包括PM,技術(shù)團(tuán)隊(duì),QA
- 名詞,明確英文術(shù)語(yǔ),為后續(xù)編碼提供類,方法,屬性等命名依據(jù)
- 避免統(tǒng)一領(lǐng)域概念的不同描述,建議的統(tǒng)一語(yǔ)言包括單不限于UML,數(shù)據(jù)表ER,接口定義。
- 領(lǐng)域行為描述
- 從領(lǐng)域的角度而非實(shí)現(xiàn)角度描述領(lǐng)域行為
- 若涉及到領(lǐng)域術(shù)語(yǔ),必須遵循術(shù)語(yǔ)表的規(guī)范
- 動(dòng)賓結(jié)構(gòu),符合業(yè)務(wù)動(dòng)作在該領(lǐng)域的合理性
限界上下文
限界上下文的含義就是用一個(gè)清晰可見的邊界(Bounded)將這個(gè)上下文勾勒出來(lái),如此就能在自己的邊界內(nèi)維持領(lǐng)域模型的一致性與完整性。
我們需要根據(jù)業(yè)務(wù)相關(guān)性、耦合的強(qiáng)弱程度、分離的關(guān)注點(diǎn)對(duì)這些活動(dòng)進(jìn)行歸類,找到不同類別之間存在的邊界,這就是限界上下文的含義。上下文(Context)是業(yè)務(wù)目標(biāo),限界(Bounded)則是保護(hù)和隔離上下文的邊界,避免業(yè)務(wù)目標(biāo)的不單一而帶來(lái)的混亂與概念的不一致。
限界上下文是一個(gè)“自治”的單元。所謂“自治”就是滿足四個(gè)特征:最小完備、穩(wěn)定空間、自我履行、獨(dú)立進(jìn)化。限界上下文,映射到編碼實(shí)現(xiàn),則可能是模塊、組件或服務(wù)。
最小完備是實(shí)現(xiàn)“自治”的基本條件。所謂“完備”,是指自治單元履行的職責(zé)是完整的,無(wú)需針對(duì)自己的信息去求助別的自治單元,這就避免了不必要的依賴關(guān)系。
自我履行意味著由自治單元自身決定要做什么。從擬人的角度來(lái)思考,就是這些自治單元能夠?qū)ν獠空?qǐng)求做出符合自身利益的明智判斷,是否應(yīng)該履行該職責(zé),由限界上下文擁有的信息來(lái)決定。
穩(wěn)定空間指的是減少外界變化對(duì)限界上下文內(nèi)部的影響。
獨(dú)立進(jìn)化與穩(wěn)定空間剛好相反,指的是減少限界上下文的變化對(duì)外界的影響。
識(shí)別限界上下文
觀察角度的不同,限界上下文劃定的邊界也有所不同。大體可以分為如下三個(gè)方面:
- 領(lǐng)域邏輯層面:限界上下文確定了領(lǐng)域模型的業(yè)務(wù)邊界,維護(hù)了模型的完整性與一致性,從而降低系統(tǒng)的業(yè)務(wù)復(fù)雜度。
- 團(tuán)隊(duì)合作層面:限界上下文確定了開發(fā)團(tuán)隊(duì)的工作邊界,建立了團(tuán)隊(duì)之間的合作模式,避免團(tuán)隊(duì)之間的溝通變得混亂,從而降低系統(tǒng)的管理復(fù)雜度。
- 技術(shù)實(shí)現(xiàn)層面:限界上下文確定了系統(tǒng)架構(gòu)的應(yīng)用邊界,保證了系統(tǒng)層和上下文領(lǐng)域?qū)痈髯缘?strong>一致性,建立了上下文之間的集成方式,從而降低系統(tǒng)的技術(shù)復(fù)雜度。
這三種邊界體現(xiàn)了限界上下文對(duì)不同邊界的控制力。業(yè)務(wù)邊界是對(duì)領(lǐng)域模型的控制,工作邊界是對(duì)開發(fā)協(xié)作的控制,應(yīng)用邊界是對(duì)技術(shù)風(fēng)險(xiǎn)的控制。引入限界上下文的目的,其實(shí)不在于如何劃分邊界,而在于如何控制邊界。
限界上下文之間的協(xié)作與通信
根據(jù)不同限界上下文的重要程度(相對(duì)業(yè)務(wù)目標(biāo)而言)、限界上下文之間的關(guān)聯(lián)強(qiáng)度(相對(duì)于用戶感知而言)、限界上下文之間的順序關(guān)系(相對(duì)于業(yè)務(wù)流程而言),采用不同的協(xié)作設(shè)計(jì)方式,盡可能減少不同限界上下文之間的同步循環(huán)依賴。
從設(shè)計(jì)的角度來(lái)講,就是不遺余力地降低限界上下文之間的耦合關(guān)系。每個(gè)限界上下文都應(yīng)該有防腐層(下游限界上下文,用以隔絕上游限界上下文可能發(fā)生的變化),針對(duì)關(guān)聯(lián)強(qiáng)度不是很緊密的盡量采用發(fā)布/訂閱事件的方式。
分離領(lǐng)域
分層的價(jià)值在于每一層都只代表程序中的某一特定方面。這種限制使每個(gè)方面的設(shè)計(jì)都更具內(nèi)聚性,更容易解釋。當(dāng)然,要分離出內(nèi)聚設(shè)計(jì)中最重要的方面,選擇恰當(dāng)?shù)姆謱臃绞绞侵陵P(guān)重要的。盡管Layered Architecture的種類繁多,但是大多數(shù)成功的架構(gòu)使用的都是下面4個(gè)概念層的某種變體。
| 層級(jí) | 說(shuō)明 |
|---|---|
| 領(lǐng)域?qū)樱ɑ蚰P蛯樱?/td> | 負(fù)責(zé)表達(dá)業(yè)務(wù)概念,業(yè)務(wù)狀態(tài)信息以及業(yè)務(wù)規(guī)則。盡管保存業(yè)務(wù)狀態(tài)的技術(shù)細(xì)節(jié)是由基礎(chǔ)設(shè)施層實(shí)現(xiàn)的,但是反映業(yè)務(wù)情況的狀態(tài)是由領(lǐng)域?qū)涌刂撇⑶沂褂玫念I(lǐng)域?qū)邮菢I(yè)務(wù)軟件的核心。 |
| 應(yīng)用層 | 定義軟件要完成的任務(wù),并且指揮表達(dá)領(lǐng)域概念的對(duì)象來(lái)解決問(wèn)題。這一層所負(fù)責(zé)的工作對(duì)業(yè)務(wù)來(lái)說(shuō)意義重大,也是與其他系統(tǒng)的應(yīng)用層進(jìn)行交互的必要渠道 |
| 用戶界面層(或表示層) | 負(fù)責(zé)向用戶顯示信息和解釋用戶指令。這里指的用戶可以是另一個(gè)計(jì)算機(jī)系統(tǒng),不一定是使用用戶界面的人 |
| 基礎(chǔ)設(shè)施層 | 為上面各層提供通用的技術(shù)能力;為應(yīng)用層傳遞消息,為領(lǐng)域?qū)犹峁┏志没瘷C(jī)制,為用戶界面層繪制屏幕組件等等?;A(chǔ)設(shè)施層還能夠通過(guò)架構(gòu)框架來(lái)支持4個(gè)層次間的交互模式。 |
如果與領(lǐng)域有關(guān)的代碼分散在大量的其他層(用戶界面層、應(yīng)用層、基礎(chǔ)設(shè)施層等)之中,那么查看與分析領(lǐng)域代碼就會(huì)變得異常困難。對(duì)用戶界面的簡(jiǎn)單修改實(shí)際上很可能會(huì)改變業(yè)務(wù)邏輯。而要想調(diào)整業(yè)務(wù)規(guī)則也可能需要對(duì)用戶界面代碼、數(shù)據(jù)庫(kù)操作代碼或者其他的程序元素進(jìn)行仔細(xì)的篩查。這樣就不太可能實(shí)現(xiàn)一致的、模型驅(qū)動(dòng)的對(duì)象了,同時(shí)也會(huì)給自動(dòng)化測(cè)試帶來(lái)困難。
給復(fù)雜的應(yīng)用程序劃分層次。在每一層內(nèi)分別進(jìn)行設(shè)計(jì),使其具有內(nèi)聚性并且只依賴于它的下層。采用標(biāo)準(zhǔn)的架構(gòu)模式,只與上層進(jìn)行松散的耦合。將所有與領(lǐng)域模型相關(guān)的代碼放在一個(gè)層中,并把它與用戶界面層、應(yīng)用層以及基礎(chǔ)設(shè)施層的代碼分開。領(lǐng)域?qū)ο髴?yīng)該將重點(diǎn)放在如何表達(dá)領(lǐng)域模型上,而不需要考慮自己的顯示和存儲(chǔ)問(wèn)題,也無(wú)需管理應(yīng)用任務(wù)等內(nèi)容。這使得模型的含義足夠豐富,結(jié)構(gòu)足夠清晰,可以捕捉到基本的業(yè)務(wù)知識(shí),并有效的使用這些知識(shí)。
各層之間是松散連接的,層與層的依賴關(guān)系只能是單向的。上層可以直接使用或操作下層元素,方法是通過(guò)調(diào)用下層元素的公共接口,保持對(duì)下層元素的引用,以及采用常規(guī)的交互手段。而如果下層元素需要與上層元素進(jìn)行通信,則需要采用另一種通信機(jī)制,使用架構(gòu)模式來(lái)連接上下層,如回調(diào)模式或觀察者模式等。
戰(zhàn)術(shù)設(shè)計(jì)
整個(gè)軟件系統(tǒng)被分解為多個(gè)限界上下文(或領(lǐng)域)后,就可以分而治之,對(duì)每個(gè)限界上下文進(jìn)行戰(zhàn)術(shù)設(shè)計(jì)。領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)并不牽涉到技術(shù)層面的實(shí)現(xiàn)細(xì)節(jié),在戰(zhàn)術(shù)層面,它主要應(yīng)對(duì)的是領(lǐng)域的復(fù)雜性。
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)用以表示模型的主要要素包括:
- 實(shí)體(Entity)
- 值對(duì)象(Value Object)
- 聚合(Aggregate)
- 工廠(Factory)
- 資源庫(kù)(Repository)
- 領(lǐng)域服務(wù)(Domain Service)
模型表示的元素
用于表示模型的3種模型元素模式:Entity、Value Object和Service。一個(gè)對(duì)象是用來(lái)表示某種具有連續(xù)性和標(biāo)識(shí)事物,還是用于描述某種狀態(tài)的屬性,這是Entity和Value Objecct之間的根本區(qū)別。
領(lǐng)域中還有一些方面適合用動(dòng)作或操作來(lái)表示,這比用對(duì)象表示更加清楚。這些方面最好用Service來(lái)表示,而不應(yīng)把操作的責(zé)任強(qiáng)加到Entity或Value Object上,盡管這樣做只是稍微違背了面向?qū)ο蟮慕鹘y(tǒng)。Service是接受客戶端請(qǐng)求來(lái)完成某事。在軟件技術(shù)層中有很多Service(這些不全是領(lǐng)域?qū)铀f(shuō)的Service)。領(lǐng)域?qū)拥囊部梢允褂肧ervice,當(dāng)對(duì)軟件要做的某項(xiàng)無(wú)狀態(tài)的活動(dòng)進(jìn)行建模時(shí),就可以將該活動(dòng)作為一項(xiàng)Service。
實(shí)體(Entity)
一些對(duì)象主要不是由它們的屬性定義的,它們實(shí)際上表示了一條“標(biāo)識(shí)線”,這些主要由標(biāo)識(shí)定義的對(duì)象被稱作Entity。Entity(實(shí)體)具有生命周期,這期間它們的形式和內(nèi)容可能發(fā)生根本改變,但必須保持一種內(nèi)在的連續(xù)性。
一個(gè)典型的實(shí)體應(yīng)該具備三個(gè)要素:
- 身份標(biāo)識(shí)
- 屬性
- 領(lǐng)域行為
值對(duì)象(Value Object)
很多對(duì)象沒有概念上的標(biāo)識(shí),但它們描述了一個(gè)事物的某種特征。用于描述領(lǐng)域的某個(gè)方面而本身沒有概念標(biāo)識(shí)的對(duì)象稱為Value Object。
值對(duì)象通常作為實(shí)體的屬性而存在,比如數(shù)量、性質(zhì)、關(guān)系、地點(diǎn)、時(shí)間與形態(tài)等范疇。是否擁有唯一的身份標(biāo)識(shí)才是實(shí)體與值對(duì)象的根本區(qū)別。
服務(wù)(Service)
有時(shí)候?qū)ο蟛皇且粋€(gè)事物,在設(shè)計(jì)中會(huì)包含一些特殊的操作,這些操作從概念上講不屬于任何對(duì)象。與其把它們強(qiáng)制地歸于哪一個(gè)類,不如順其自然地在模型中引入一種新的元素,這就是Service(服務(wù))。一些領(lǐng)域改變不適合被建模為對(duì)象。如果勉強(qiáng)把這些重要的領(lǐng)域功能歸為Entity或Value Object的職責(zé),那么不是歪曲基于建模的對(duì)象定義,就是人為增加一些無(wú)意義的對(duì)象。比如賬戶之間的轉(zhuǎn)賬,用轉(zhuǎn)賬服務(wù)來(lái)描述比將轉(zhuǎn)賬行為分解到賬戶中更加自然。
使用Service時(shí)應(yīng)謹(jǐn)慎,它們不應(yīng)該替代Entity和Value Object的所有行為。但是,一個(gè)操作實(shí)際上是一個(gè)重要的領(lǐng)域概念時(shí),Service很自然就會(huì)稱為Model-Driven Design中的一部分。
好的Service應(yīng)該有以下3個(gè)特征:
- 與領(lǐng)域概念相關(guān)的不是Entity或Value Object 的一個(gè)自然組成部分。
- 接口是根據(jù)領(lǐng)域模型的其他元素定義的。
- 操作是無(wú)狀態(tài),這里說(shuō)得無(wú)狀態(tài)是指任何客戶都可以使用某個(gè)Service的任何實(shí)例,而不必關(guān)心該實(shí)例的歷史狀態(tài)。
領(lǐng)域?qū)ο蟮纳芷?/h4>
領(lǐng)域?qū)ο蟮纳芷?/div>
管理這些對(duì)象時(shí)面臨諸多挑戰(zhàn),稍有不慎就會(huì)偏離Model-Driven Design的軌道。主要的挑戰(zhàn)有以下兩類:
- 在整個(gè)生命周期中維護(hù)完整性和事務(wù)。
- 防止模型陷入管理生命周期復(fù)雜性造成的困境當(dāng)中。
通過(guò)3種模式解決這些問(wèn)題,1)首先是Aggregate(聚合),它通過(guò)定義清晰的所屬關(guān)系和邊界,并避免錯(cuò)綜復(fù)雜的對(duì)象關(guān)系網(wǎng)來(lái)實(shí)現(xiàn)模型的內(nèi)聚。聚合模式對(duì)于維護(hù)生命周期各個(gè)階段的完整性具有至關(guān)重要的作用。2)在生命周期的開始階段,使用Factory(工廠)來(lái)創(chuàng)建和重建復(fù)雜對(duì)象,從而封裝它們的內(nèi)部結(jié)構(gòu)。3)在生命周期的中間和末尾使用Repository(存儲(chǔ)庫(kù))來(lái)提供查找和檢索持久化對(duì)象并封裝龐大基礎(chǔ)設(shè)施的手段。盡管Repository和Factory本身并不是來(lái)源于領(lǐng)域,但它們?cè)陬I(lǐng)域設(shè)計(jì)中扮演者重要的角色。
(聚合)Aggregate
Aggregate就是一組相關(guān)對(duì)象的集合,我們把它作為數(shù)據(jù)修改的單元。每個(gè)Aggregate都有一個(gè)根(root)和一個(gè)邊界(boundary)。根是Aggregate所包含的一個(gè)特定Entity。對(duì)Aggregate而言,外部對(duì)象只可以引用根,而邊界內(nèi)部的對(duì)象之間則可以互相引用。
為了實(shí)現(xiàn)這個(gè)概念上的Aggregate,需要對(duì)所有事務(wù)應(yīng)用一組規(guī)則。
- 根Entity具有全局標(biāo)識(shí),它最終負(fù)責(zé)檢查固定規(guī)則。
- 根Entity具有全局標(biāo)識(shí)。邊界內(nèi)的Entity具有本地標(biāo)識(shí),這些標(biāo)識(shí)只在Aggregate內(nèi)部才是唯一的。
- Aggregate外部的對(duì)象不能引用除Entity之外的任何內(nèi)部對(duì)象。根Entity可以把內(nèi)部Entity的引用傳給它們,但這些對(duì)象只能臨時(shí)使用這些引用,而不能保持引用。根可以把一個(gè)Value Object的副本傳給另一個(gè)對(duì)象,而不必關(guān)系它發(fā)生什么變化,因?yàn)樗皇且粋€(gè)Value,不再與Aggregate有任何關(guān)聯(lián)。
- 作為上一條規(guī)則的推論,只有Aggregate的根才能直接通過(guò)數(shù)據(jù)庫(kù)查詢獲取。所有其他對(duì)象必須通過(guò)關(guān)聯(lián)來(lái)發(fā)現(xiàn)。
- Aggregate內(nèi)部的對(duì)象可以保持對(duì)其他Aggregate根的引用。
- 刪除操作必須一次刪除Aggregate邊界之內(nèi)的所有對(duì)象。
- 當(dāng)提交對(duì)Aggregate邊界內(nèi)部的任何對(duì)象的修改時(shí),整個(gè)Aggregate的所有固定規(guī)則都必須被滿足。
工廠(Factory)
當(dāng)創(chuàng)建一個(gè)對(duì)象或創(chuàng)建整個(gè)Aggregate時(shí),如果創(chuàng)建工作很復(fù)雜,或者暴露了過(guò)多的內(nèi)部結(jié)構(gòu),則可以使用Factory進(jìn)行封裝。
復(fù)雜的對(duì)象創(chuàng)建是領(lǐng)域?qū)拥穆氊?zé),然而這項(xiàng)任務(wù)并不屬于那些用于表示模型的對(duì)象。當(dāng)客戶負(fù)責(zé)創(chuàng)建復(fù)雜對(duì)象時(shí),它會(huì)牽涉不必要的復(fù)雜性,并將其職責(zé)搞的模糊不清。這違背了領(lǐng)域?qū)ο蠹八鶆?chuàng)建的Aggregate的封裝要求。更嚴(yán)重的是,如果客戶是應(yīng)用的一部分,那么職責(zé)就會(huì)從領(lǐng)域?qū)有孤┑綉?yīng)用層中。應(yīng)用層與實(shí)現(xiàn)細(xì)節(jié)之間的這種耦合使得領(lǐng)域?qū)映橄蟠蟛糠謨?yōu)勢(shì)蕩然無(wú)存,而且導(dǎo)致后續(xù)更改的代價(jià)變得更加高昂。
Factory就是一種負(fù)責(zé)創(chuàng)建其他對(duì)象的構(gòu)造機(jī)制,封裝了創(chuàng)建復(fù)雜對(duì)象或Aggregate所需的知識(shí)。Factory提供了反映客戶目標(biāo)的接口,以及被創(chuàng)建對(duì)象的抽象視圖,從而使客戶無(wú)需知道對(duì)象的工作機(jī)理就可以使用對(duì)象的功能。
Factory有很多設(shè)計(jì)方式,包括但不限于工廠方法模式、抽象工廠模式和構(gòu)造器模式。任何好的工廠都需要滿足以下兩個(gè)基本需求:
- 每個(gè)創(chuàng)建方法都是原子的,而且要保證被創(chuàng)建對(duì)象或Aggregate的所有固定規(guī)則。Factory生成的對(duì)象要處于一致的狀態(tài)。在生成Entity時(shí),意味著創(chuàng)建滿足所有固定規(guī)則的整個(gè)Aggregate,但在創(chuàng)建完成后可以向Aggregate添加可選元素。
- Factory應(yīng)該被抽象為所需的類型,而不是所要?jiǎng)?chuàng)建的具體類。
在向Aggregate添加元素時(shí)可以通過(guò)在Aggregate的根上創(chuàng)建一個(gè)Factory Method,從而把Aggregate的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)隱藏起來(lái),使任何外部客戶看不到這些細(xì)節(jié),同時(shí)使根負(fù)責(zé)確保Aggregate在添加元素的完整性。
Factory與被構(gòu)造對(duì)象之間是緊密耦合的,因此Factory應(yīng)該只被關(guān)聯(lián)到與被構(gòu)造對(duì)象有著密切聯(lián)系的對(duì)象上。當(dāng)有些細(xì)節(jié)需要隱藏而又找不到合適的地方來(lái)隱藏它們時(shí),必須創(chuàng)建一個(gè)專用的Factory對(duì)象或Service。整個(gè)Aggregate通常由一個(gè)獨(dú)立的Factory來(lái)創(chuàng)建,F(xiàn)actory負(fù)責(zé)把對(duì)根的引用傳遞出去,并確保創(chuàng)建出的Aggregate滿足固定規(guī)則。如果Aggregate內(nèi)部的某個(gè)對(duì)象需要一個(gè)Factory,而這個(gè)Factory又不適合在Aggregate根上創(chuàng)建,那么應(yīng)該構(gòu)建一個(gè)獨(dú)立的Factory。
資源庫(kù)(Repository)
Factory封裝了對(duì)象創(chuàng)建和重建時(shí)的生命周期轉(zhuǎn)換。還有一種轉(zhuǎn)換大大增加了領(lǐng)域設(shè)計(jì)的技術(shù)復(fù)雜性,就是對(duì)象與存儲(chǔ)之間的互相轉(zhuǎn)換。這種轉(zhuǎn)換由另一種領(lǐng)域設(shè)計(jì)構(gòu)造來(lái)處理,它就是Repository。
Repository將某種類型的所有對(duì)象表示為一個(gè)概念集合(通常是模擬的)。它的行為類似于集合,只是具有更復(fù)雜的查詢功能。
Aggregate根提供Repository,讓客戶始終聚焦于模型,而將所有對(duì)象的存儲(chǔ)和訪問(wèn)操作封裝起來(lái),在Repository里來(lái)完成。Respository有很多優(yōu)點(diǎn),包括:
- 它們?yōu)榭蛻籼峁┝艘粋€(gè)簡(jiǎn)單的模型,可用來(lái)獲取持久化對(duì)象并管理它們的生命周期。
- 它們使應(yīng)用程序和領(lǐng)域設(shè)計(jì)與持久化技術(shù)(多種數(shù)據(jù)庫(kù)策略甚至是多個(gè)數(shù)據(jù)源)解構(gòu)。
- 它們體現(xiàn)了有關(guān)對(duì)象訪問(wèn)的設(shè)計(jì)決策
- 可以很容易將它們替換為“啞實(shí)現(xiàn)”,以便在測(cè)試中使用(通常使用內(nèi)存中的集合)
參考
- 《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》Vaughn Vernon
gitchat:領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)實(shí)踐(戰(zhàn)略篇),需要付費(fèi)購(gòu)買, 張逸
gitchat:領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)實(shí)踐(戰(zhàn)術(shù)篇),需要付費(fèi)購(gòu)買, 張逸
最后編輯于 :?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。相關(guān)閱讀更多精彩內(nèi)容
- 道可道,非常道.名可名,非常名.無(wú)名天地之始,有名萬(wàn)物之母.---老子 關(guān)于標(biāo)題 好久沒寫東西了,動(dòng)筆的動(dòng)機(jī)是看完...
- 1. 什么是領(lǐng)域(Domain) 我們所做的軟件系統(tǒng)的目的都是來(lái)解決一系列問(wèn)題,例如做一個(gè)電商系統(tǒng)來(lái)在線銷售自己...
- 論代碼的表達(dá)能力 一直想向部門的同事們推薦領(lǐng)域驅(qū)動(dòng)的設(shè)計(jì)方法,但是一直找不到一個(gè)合適的切入點(diǎn)。直到某年某月某日,看...
- 前言 至少30年以前,一些軟件設(shè)計(jì)人員就已經(jīng)意識(shí)到領(lǐng)域建模和設(shè)計(jì)的重要性,并形成一種思潮,Eric Evans將其...
- 那是個(gè)周五的下午,我反復(fù)的翻弄著最后一位病人的預(yù)約單。原本期待能在6點(diǎn)前早早下班享受周末,哪知這最后一位病人遲遲沒...

管理這些對(duì)象時(shí)面臨諸多挑戰(zhàn),稍有不慎就會(huì)偏離Model-Driven Design的軌道。主要的挑戰(zhàn)有以下兩類:
- 在整個(gè)生命周期中維護(hù)完整性和事務(wù)。
- 防止模型陷入管理生命周期復(fù)雜性造成的困境當(dāng)中。
通過(guò)3種模式解決這些問(wèn)題,1)首先是Aggregate(聚合),它通過(guò)定義清晰的所屬關(guān)系和邊界,并避免錯(cuò)綜復(fù)雜的對(duì)象關(guān)系網(wǎng)來(lái)實(shí)現(xiàn)模型的內(nèi)聚。聚合模式對(duì)于維護(hù)生命周期各個(gè)階段的完整性具有至關(guān)重要的作用。2)在生命周期的開始階段,使用Factory(工廠)來(lái)創(chuàng)建和重建復(fù)雜對(duì)象,從而封裝它們的內(nèi)部結(jié)構(gòu)。3)在生命周期的中間和末尾使用Repository(存儲(chǔ)庫(kù))來(lái)提供查找和檢索持久化對(duì)象并封裝龐大基礎(chǔ)設(shè)施的手段。盡管Repository和Factory本身并不是來(lái)源于領(lǐng)域,但它們?cè)陬I(lǐng)域設(shè)計(jì)中扮演者重要的角色。
(聚合)Aggregate
Aggregate就是一組相關(guān)對(duì)象的集合,我們把它作為數(shù)據(jù)修改的單元。每個(gè)Aggregate都有一個(gè)根(root)和一個(gè)邊界(boundary)。根是Aggregate所包含的一個(gè)特定Entity。對(duì)Aggregate而言,外部對(duì)象只可以引用根,而邊界內(nèi)部的對(duì)象之間則可以互相引用。
為了實(shí)現(xiàn)這個(gè)概念上的Aggregate,需要對(duì)所有事務(wù)應(yīng)用一組規(guī)則。
- 根Entity具有全局標(biāo)識(shí),它最終負(fù)責(zé)檢查固定規(guī)則。
- 根Entity具有全局標(biāo)識(shí)。邊界內(nèi)的Entity具有本地標(biāo)識(shí),這些標(biāo)識(shí)只在Aggregate內(nèi)部才是唯一的。
- Aggregate外部的對(duì)象不能引用除Entity之外的任何內(nèi)部對(duì)象。根Entity可以把內(nèi)部Entity的引用傳給它們,但這些對(duì)象只能臨時(shí)使用這些引用,而不能保持引用。根可以把一個(gè)Value Object的副本傳給另一個(gè)對(duì)象,而不必關(guān)系它發(fā)生什么變化,因?yàn)樗皇且粋€(gè)Value,不再與Aggregate有任何關(guān)聯(lián)。
- 作為上一條規(guī)則的推論,只有Aggregate的根才能直接通過(guò)數(shù)據(jù)庫(kù)查詢獲取。所有其他對(duì)象必須通過(guò)關(guān)聯(lián)來(lái)發(fā)現(xiàn)。
- Aggregate內(nèi)部的對(duì)象可以保持對(duì)其他Aggregate根的引用。
- 刪除操作必須一次刪除Aggregate邊界之內(nèi)的所有對(duì)象。
- 當(dāng)提交對(duì)Aggregate邊界內(nèi)部的任何對(duì)象的修改時(shí),整個(gè)Aggregate的所有固定規(guī)則都必須被滿足。
工廠(Factory)
當(dāng)創(chuàng)建一個(gè)對(duì)象或創(chuàng)建整個(gè)Aggregate時(shí),如果創(chuàng)建工作很復(fù)雜,或者暴露了過(guò)多的內(nèi)部結(jié)構(gòu),則可以使用Factory進(jìn)行封裝。
復(fù)雜的對(duì)象創(chuàng)建是領(lǐng)域?qū)拥穆氊?zé),然而這項(xiàng)任務(wù)并不屬于那些用于表示模型的對(duì)象。當(dāng)客戶負(fù)責(zé)創(chuàng)建復(fù)雜對(duì)象時(shí),它會(huì)牽涉不必要的復(fù)雜性,并將其職責(zé)搞的模糊不清。這違背了領(lǐng)域?qū)ο蠹八鶆?chuàng)建的Aggregate的封裝要求。更嚴(yán)重的是,如果客戶是應(yīng)用的一部分,那么職責(zé)就會(huì)從領(lǐng)域?qū)有孤┑綉?yīng)用層中。應(yīng)用層與實(shí)現(xiàn)細(xì)節(jié)之間的這種耦合使得領(lǐng)域?qū)映橄蟠蟛糠謨?yōu)勢(shì)蕩然無(wú)存,而且導(dǎo)致后續(xù)更改的代價(jià)變得更加高昂。
Factory就是一種負(fù)責(zé)創(chuàng)建其他對(duì)象的構(gòu)造機(jī)制,封裝了創(chuàng)建復(fù)雜對(duì)象或Aggregate所需的知識(shí)。Factory提供了反映客戶目標(biāo)的接口,以及被創(chuàng)建對(duì)象的抽象視圖,從而使客戶無(wú)需知道對(duì)象的工作機(jī)理就可以使用對(duì)象的功能。
Factory有很多設(shè)計(jì)方式,包括但不限于工廠方法模式、抽象工廠模式和構(gòu)造器模式。任何好的工廠都需要滿足以下兩個(gè)基本需求:
- 每個(gè)創(chuàng)建方法都是原子的,而且要保證被創(chuàng)建對(duì)象或Aggregate的所有固定規(guī)則。Factory生成的對(duì)象要處于一致的狀態(tài)。在生成Entity時(shí),意味著創(chuàng)建滿足所有固定規(guī)則的整個(gè)Aggregate,但在創(chuàng)建完成后可以向Aggregate添加可選元素。
- Factory應(yīng)該被抽象為所需的類型,而不是所要?jiǎng)?chuàng)建的具體類。
在向Aggregate添加元素時(shí)可以通過(guò)在Aggregate的根上創(chuàng)建一個(gè)Factory Method,從而把Aggregate的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)隱藏起來(lái),使任何外部客戶看不到這些細(xì)節(jié),同時(shí)使根負(fù)責(zé)確保Aggregate在添加元素的完整性。
Factory與被構(gòu)造對(duì)象之間是緊密耦合的,因此Factory應(yīng)該只被關(guān)聯(lián)到與被構(gòu)造對(duì)象有著密切聯(lián)系的對(duì)象上。當(dāng)有些細(xì)節(jié)需要隱藏而又找不到合適的地方來(lái)隱藏它們時(shí),必須創(chuàng)建一個(gè)專用的Factory對(duì)象或Service。整個(gè)Aggregate通常由一個(gè)獨(dú)立的Factory來(lái)創(chuàng)建,F(xiàn)actory負(fù)責(zé)把對(duì)根的引用傳遞出去,并確保創(chuàng)建出的Aggregate滿足固定規(guī)則。如果Aggregate內(nèi)部的某個(gè)對(duì)象需要一個(gè)Factory,而這個(gè)Factory又不適合在Aggregate根上創(chuàng)建,那么應(yīng)該構(gòu)建一個(gè)獨(dú)立的Factory。
資源庫(kù)(Repository)
Factory封裝了對(duì)象創(chuàng)建和重建時(shí)的生命周期轉(zhuǎn)換。還有一種轉(zhuǎn)換大大增加了領(lǐng)域設(shè)計(jì)的技術(shù)復(fù)雜性,就是對(duì)象與存儲(chǔ)之間的互相轉(zhuǎn)換。這種轉(zhuǎn)換由另一種領(lǐng)域設(shè)計(jì)構(gòu)造來(lái)處理,它就是Repository。
Repository將某種類型的所有對(duì)象表示為一個(gè)概念集合(通常是模擬的)。它的行為類似于集合,只是具有更復(fù)雜的查詢功能。
Aggregate根提供Repository,讓客戶始終聚焦于模型,而將所有對(duì)象的存儲(chǔ)和訪問(wèn)操作封裝起來(lái),在Repository里來(lái)完成。Respository有很多優(yōu)點(diǎn),包括:
- 它們?yōu)榭蛻籼峁┝艘粋€(gè)簡(jiǎn)單的模型,可用來(lái)獲取持久化對(duì)象并管理它們的生命周期。
- 它們使應(yīng)用程序和領(lǐng)域設(shè)計(jì)與持久化技術(shù)(多種數(shù)據(jù)庫(kù)策略甚至是多個(gè)數(shù)據(jù)源)解構(gòu)。
- 它們體現(xiàn)了有關(guān)對(duì)象訪問(wèn)的設(shè)計(jì)決策
- 可以很容易將它們替換為“啞實(shí)現(xiàn)”,以便在測(cè)試中使用(通常使用內(nèi)存中的集合)
參考
- 《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》Vaughn Vernon
gitchat:領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)實(shí)踐(戰(zhàn)略篇),需要付費(fèi)購(gòu)買, 張逸
gitchat:領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)實(shí)踐(戰(zhàn)術(shù)篇),需要付費(fèi)購(gòu)買, 張逸
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
相關(guān)閱讀更多精彩內(nèi)容
- 道可道,非常道.名可名,非常名.無(wú)名天地之始,有名萬(wàn)物之母.---老子 關(guān)于標(biāo)題 好久沒寫東西了,動(dòng)筆的動(dòng)機(jī)是看完...
- 1. 什么是領(lǐng)域(Domain) 我們所做的軟件系統(tǒng)的目的都是來(lái)解決一系列問(wèn)題,例如做一個(gè)電商系統(tǒng)來(lái)在線銷售自己...
- 論代碼的表達(dá)能力 一直想向部門的同事們推薦領(lǐng)域驅(qū)動(dòng)的設(shè)計(jì)方法,但是一直找不到一個(gè)合適的切入點(diǎn)。直到某年某月某日,看...
- 前言 至少30年以前,一些軟件設(shè)計(jì)人員就已經(jīng)意識(shí)到領(lǐng)域建模和設(shè)計(jì)的重要性,并形成一種思潮,Eric Evans將其...
- 那是個(gè)周五的下午,我反復(fù)的翻弄著最后一位病人的預(yù)約單。原本期待能在6點(diǎn)前早早下班享受周末,哪知這最后一位病人遲遲沒...