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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

领域设计基本理论知识总结(转)

發(fā)布時間:2025/3/21 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 领域设计基本理论知识总结(转) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文非原創(chuàng),轉(zhuǎn)自博客園:http://www.cnblogs.com/netfocus/archive/2011/10/10/2204949.html

領(lǐng)域驅(qū)動設(shè)計值領(lǐng)域模型

2004年Eric Evans發(fā)表Domain-Driven Design-Tackling Complexity in the Heart of Software(領(lǐng)域驅(qū)動設(shè)計),簡稱為Evans DDD。領(lǐng)域驅(qū)動設(shè)計分為兩個階段:

1、以一種領(lǐng)域?qū)<摇⒃O(shè)計人員、開發(fā)人員能理解的“通用語言”作為互相交流的工具,在不斷交流的過程不斷發(fā)現(xiàn)一些主要的領(lǐng)域概念,然后將這些概念設(shè)計成領(lǐng)域模型;

2、由領(lǐng)域模型驅(qū)動設(shè)計,用代碼來表現(xiàn)該領(lǐng)域模型。

由此可見,領(lǐng)域驅(qū)動設(shè)計的核心是建立領(lǐng)域模型。領(lǐng)域模型在軟件架構(gòu)中處于核心地位;軟件開發(fā)過程中,必須以機那里領(lǐng)域模型為中心。

為什么建立一個領(lǐng)域模型是重要的

領(lǐng)域驅(qū)動設(shè)計告訴我們,在通過軟件實現(xiàn)一個業(yè)務(wù)系統(tǒng)時,建立一個領(lǐng)域模型是非常重要的和必要的,因為領(lǐng)域模型具有以下特點:

1、領(lǐng)域模型對具有某個邊界的領(lǐng)域的一個抽象,反映了領(lǐng)域內(nèi)用戶業(yè)務(wù)需求的本質(zhì);領(lǐng)域模型是具有邊界的,只反映了我們在領(lǐng)域內(nèi)所關(guān)注的部分;

2、領(lǐng)域模型只反映業(yè)務(wù),和任何技術(shù)實現(xiàn)無關(guān);領(lǐng)域模型不僅能反映領(lǐng)域中的一些實體概念,如貨物、書本、應(yīng)聘記錄、地址等;還能反映領(lǐng)域中的一些過程概念,如資金周轉(zhuǎn)、貨物租賃的等;

3、領(lǐng)域模型確保了我們的軟件的業(yè)務(wù)邏輯都在一個模型中,都在一個地方;這樣對提高軟件的可維護(hù)性,業(yè)務(wù)科理解性以及可重用性方面都有很好的幫助;

4、領(lǐng)域模型能夠幫助開發(fā)人員相對平滑的將領(lǐng)域知識轉(zhuǎn)化為軟件構(gòu)造;

5、領(lǐng)域模型貫穿軟件分析、設(shè)計,以及開發(fā)的整個過程;領(lǐng)域?qū)<摇⒃O(shè)計人員、開發(fā)人員通過領(lǐng)域模型進(jìn)行交流,彼此共享知識與信息;因為大家面向的都是同一個模型,所以可以防止需求走樣,可以讓軟件開發(fā)人員做出來的軟件真正的滿足需求;

6、要建立正確的領(lǐng)域模型并不簡單,需要領(lǐng)域?qū)<摇⒃O(shè)計、開發(fā)人員積極溝通共同努力,然后才能使大家対領(lǐng)域的認(rèn)識不斷深入,從而不斷細(xì)化和完善領(lǐng)域模型;

7、為了讓領(lǐng)域模型看的見,我們需要一些方法來表示;圖時表達(dá)領(lǐng)域模型最常用的方式,但不是唯一的表達(dá)方式,代碼或文字描述也能表達(dá)領(lǐng)域模型;

8、領(lǐng)域模型是整個軟件的核心,是軟件中最有價值和最具競爭力的部分;設(shè)計足夠精良且符合業(yè)務(wù)需求的領(lǐng)域模型能夠更快速的響應(yīng)需求變化。

領(lǐng)域通用語言(Ubiquitous Language)

? 我們認(rèn)識到軟件專家和領(lǐng)域?qū)<彝献鏖_發(fā)一個領(lǐng)域的模型是絕對需要的,但是,那種方法通常會由于一些基礎(chǔ)交流的障礙而存在難點。開發(fā)人員滿腦子都是類、方法、算法、模式、架構(gòu)等等,總是想將實際生活中的概念和程序工作進(jìn)行對應(yīng)。他們希望看到要建立那些對象類,要如何對對象類之間的關(guān)系建模。他們會習(xí)慣按照封裝、繼承、多態(tài)等面向?qū)ο缶幊讨械母拍钊ニ伎?#xff0c;會隨時隨地的這樣交談,這對我們來說這太正常不過了,開發(fā)人員就是開發(fā)人員。但是領(lǐng)域?qū)<彝ǔ@一無所知,我們對軟件類庫、框架、持久化甚至數(shù)據(jù)庫都沒有概念。他們只了解他們特有的領(lǐng)域?qū)I(yè)技能。比如,在空中交通監(jiān)控用例中,領(lǐng)域?qū)<抑里w機、路線、海拔、經(jīng)度、知道飛機偏離了正常路線,知道飛機的起飛。他們用他們自己的術(shù)語討論這些事情,有時對于外來來說很難理解。如果一個人說了什么事情,其他的人不能理解,或者更糟的是錯誤理解成其他事情,又有什么機會來保證項目成功呢?

?? 在交流過程中,需要做的飯翻譯才能讓其他的人理解這些概念。開發(fā)人員可能會努力使用外行人的語言來解析一些設(shè)計模式,但這并非一定能成功奏效。領(lǐng)域?qū)<乙部赡軙?chuàng)建一種新的行話以努力表達(dá)他們的這些想法。在這個痛苦的交流過程中,這種類型的翻譯并不能對知識的構(gòu)建過程產(chǎn)生幫助。

?? 領(lǐng)域驅(qū)動設(shè)計的一個核心的原則是使用一種基于模型的語言。因為模型是軟件滿足領(lǐng)域的共同點,它很適合作為這種通用語言的構(gòu)造基礎(chǔ)。使用模型作為語言的核心骨架,要求團隊在進(jìn)行所有的交流是都使用一致的語言,在代碼中也是這樣。在共享知識和推敲模型時,團隊會使用演講、文字和圖像。這就需呀確保團隊使用的語言在所有的交流形式中看上去都是一致的,這種語言被稱為“通用語言(Ubiquitous Language)”。通用語言應(yīng)該在建模過程中廣泛嘗試以推動軟件專家和領(lǐng)域?qū)<抑g的溝通,從而發(fā)現(xiàn)現(xiàn)在模型中使用的主要的是領(lǐng)域概念。

將領(lǐng)域模型轉(zhuǎn)換為代碼實現(xiàn)的最佳實踐

? 擁有一個看上去的模型不代表模型能被直接轉(zhuǎn)換成代碼,也或者它的實現(xiàn)可能會違背某些我們所不建議的軟件設(shè)計原則。我們該如何實現(xiàn)從領(lǐng)域模型到代碼的轉(zhuǎn)換,并讓代碼具有可擴展性、可維護(hù)性、高性能等指標(biāo)呢?另外,如實反映領(lǐng)域的模型可能會導(dǎo)致對象持久化的一系列問題,或?qū)е虏豢山邮艿男阅軉栴}。那么我們該如何做呢?

??我們應(yīng)該緊密關(guān)聯(lián)領(lǐng)域和設(shè)計,緊密將領(lǐng)域模型和軟件編碼實現(xiàn)捆綁在一起,模型在構(gòu)建時就考慮到軟件和設(shè)計。開發(fā)人員會被加入到建模的過程中來。主要的想法是選擇一個能夠恰當(dāng)在軟件中表現(xiàn)的模型,這樣設(shè)計過程很順暢并且基于模型。代碼和其下的模型緊密關(guān)聯(lián)讓代碼更有意義并與模型更相關(guān)。有了開發(fā)人員的參與就會有了反饋。它能保證模型能被實現(xiàn)成軟件。如果其中某處有錯誤,會在早起的被標(biāo)示出來,問題也會容易修正。寫代碼的人會很好的了解模型,會感覺自己有責(zé)任保持它的完整性。他們會意識到對代碼的一個變更其實隱含著對模型的更改,另外,如果那里的代碼不能表現(xiàn)原始模型的話,他們會重構(gòu)代碼。如果分析人員從實現(xiàn)過程中分離出去,他會不再關(guān)心開發(fā)過程中引入的局限性。最終結(jié)果是模型不再實用。任何技術(shù)人員想對模型做出貢獻(xiàn)必須花費一些世界來接觸代碼,無論他咋項目中擔(dān)負(fù)的是什么主要角色。任何一個負(fù)責(zé)修改代碼的人都必須學(xué)會用代碼表現(xiàn)模型。每位開發(fā)人員都必須參與到一定級別的領(lǐng)域討論中并和領(lǐng)域?qū)<衣?lián)絡(luò)。

領(lǐng)域建模時思考問題的角度

“用戶需求”不能等同于“用戶”,捕獲“用戶心中的模型”也不能等同于“以用戶為核心設(shè)計領(lǐng)域模型”。《老子》書中有個觀點:有之以為利,無之以為用。在這里,有之利,即建領(lǐng)域模型;無之用,即包容用戶需求。舉些例子,一個杯子要裝滿一杯水,我們在制作杯子時,制作的是空杯子,既要把水倒出來,之后才能裝下水;再比如,一座房子要住人,我們在建房子時,建造的房子是空的,唯有空的才能容納人的居住。因此,建立領(lǐng)域模型時也要將用戶置于模型之外,這樣才能包容用戶的需求。

?所以,我的理解是:

1、我們設(shè)計領(lǐng)域模型時不能以用戶作為中心為出發(fā)點去思考問題,不能老是想著用戶會對系統(tǒng)做什么;而應(yīng)該從一個客觀的角度,根據(jù)用戶需求挖掘出領(lǐng)域內(nèi)的相關(guān)事物,思考這些事物的本質(zhì)關(guān)聯(lián)以及變化規(guī)律作為思考的出發(fā)點去思考問題。

2、領(lǐng)域模型是排除了人之外的客觀世界模型,但是領(lǐng)域模型包含人所扮演的參與者角色,但是一般情況下不要讓參與者角色在領(lǐng)域模型中占據(jù)主要位置,如果以人所扮演的參與者角色在領(lǐng)域模型中占據(jù)主要位置,那么各個系統(tǒng)的領(lǐng)域模型將變得沒有差別,因為軟件系統(tǒng)就是一個人機交互系統(tǒng),都是以人為主的活動記錄或跟蹤;比如:論壇中如果以人為主導(dǎo),那么領(lǐng)域模型就是:人發(fā)帖,人回帖,人結(jié)貼等等;DDD的例子中,如果是以人為中心的話,就變成了:托運人托運貨物,收貨人收獲物,付款人付款等,因此,當(dāng)我們談及領(lǐng)域模型時,已經(jīng)默認(rèn)把人的因素排除開了,因為領(lǐng)域只對于人來說才有意義,人在領(lǐng)域范圍之外的,如果人也劃入領(lǐng)域、領(lǐng)域模型很難保持客觀性。領(lǐng)域模型是與誰用和怎樣用是無關(guān)的的客觀模型。歸納起來說就是,領(lǐng)域建模是建立虛擬模型讓我們現(xiàn)實的人使用,而不是建立虛擬空間,去模仿現(xiàn)實。

以Eric Evans(DDD之父)在他的書中的一個貨運系統(tǒng)為例子簡單說明一下。

在經(jīng)過一些用戶需求討論之后,在用戶需求相對明朗之后,Eric這樣描述領(lǐng)域模型:

1、一個Cargo(貨物)涉及多個Customer(客戶、如:托運人、收貨人、收款人),每個Customer承擔(dān)不同的角色。

2、Cargo的運送目標(biāo)已指定,即Cargo有一個運送目標(biāo);

3、由一系列滿足Specification(規(guī)格)和Carrier Movement(運輸動作)來完成運輸目標(biāo)。

從上面的描述我們可以看出,他完全沒有從用戶角度去描述模型,而是以領(lǐng)域內(nèi)的相關(guān)事物為出發(fā)點,考慮這些事物的本質(zhì)關(guān)聯(lián)以及其變化規(guī)律的。上述這段描述完全以貨物為中心,把客戶看成是貨物在某個場景中可能會設(shè)計到的關(guān)聯(lián)角色,如貨物會設(shè)計到托運人、收貨人、付款人;貨物有一個確定的目標(biāo),貨物會經(jīng)過一系列的運輸動作到達(dá)目的地;其實,我覺得以用戶為中心來思考領(lǐng)域模型的思維只是停留在需求的表面,而沒有挖掘出真正的需求的本質(zhì);我們在做領(lǐng)域建模時需要努力挖掘用戶需求的本質(zhì),這樣才能真正實現(xiàn)用戶需求。

? 關(guān)于用戶、參與者這兩個概念的區(qū)分,可以看一下下面的例子:

試想兩個人共同玩足球游戲,操作者(用戶)是驅(qū)動者,它促使足球比賽領(lǐng)域中,各個“人”(參與者)的活動。這里立下一個假設(shè),假設(shè)操作者A操作某一隊員a,而隊員a擁有者某人B的信息,那么有以下說法,a是B的鏡像,a是領(lǐng)域參與者,A是驅(qū)動者。

領(lǐng)域驅(qū)動設(shè)計的標(biāo)準(zhǔn)分層架構(gòu)

1、用戶界面/展現(xiàn)層

負(fù)責(zé)向用戶展現(xiàn)信息以及解釋用戶命令。更細(xì)的方面來講就是:

? <1>請求應(yīng)用層以獲取用戶所需要展現(xiàn)的數(shù)據(jù);

? <2>發(fā)送命令給應(yīng)用層要求執(zhí)行某個用戶命令。

2、應(yīng)用層

?? 很薄的一層,定義軟件要完成的所有任務(wù)。對外為展現(xiàn)層提供各種應(yīng)用功能(包括查詢或命令),對內(nèi)調(diào)用領(lǐng)域?qū)?#xff08;領(lǐng)域?qū)ο蠡蝾I(lǐng)域服務(wù))完成各種業(yè)務(wù)邏輯,應(yīng)用層不包含業(yè)務(wù)邏輯,這里一般采用門面模式(Facade)來隱藏底層的實現(xiàn)。

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

負(fù)責(zé)表達(dá)業(yè)務(wù)概念,業(yè)務(wù)狀態(tài)信息以及業(yè)務(wù)規(guī)則,領(lǐng)域模型處于這一層,是業(yè)務(wù)軟件的核心

4、基礎(chǔ)設(shè)施層

本層為其它層提供通用的技術(shù)能力;提供了層間的通信;為領(lǐng)域?qū)訉崿F(xiàn)數(shù)據(jù)持久化;總之,基礎(chǔ)設(shè)施層可以通過架構(gòu)和框架來支持其他層的技術(shù)需求。

領(lǐng)域驅(qū)動設(shè)計過程使用的模式

所有模式的概覽圖:

關(guān)聯(lián)的設(shè)計

? 關(guān)聯(lián)本身不是一個模式,但它在領(lǐng)域建模的過程中非常重要,所以需要在探討各種模式之前,先討論一下對象之間的關(guān)聯(lián)如何設(shè)計。我覺得對象的關(guān)聯(lián)的設(shè)計可以遵循如下的一些原則:

1、關(guān)聯(lián)盡量少,對象之間的復(fù)雜的關(guān)聯(lián)容易形成對象的關(guān)系網(wǎng),這樣對于我們理解和維護(hù)維護(hù)單個對象很不利,同時也很難劃分對象與對象之間的邊界;另外,同時減少關(guān)聯(lián)有助于簡化對象之間的遍歷。

2、一對多的關(guān)聯(lián)也許在業(yè)務(wù)上是很自然的,通常我們會用一個集合來表示一對多的關(guān)系但我們往往也考慮到性能問題,尤其當(dāng)集合內(nèi)元素非常多的時候,此時往往需要通過單獨查詢來獲取關(guān)聯(lián)的集合信息,比如懶加載。

3、關(guān)聯(lián)盡量保持單向的關(guān)聯(lián);

4、在建立關(guān)聯(lián)時,我們需要深入去挖掘是否存在關(guān)聯(lián)的限制條件,如果存在,那么最好把這個限制條件加到這個關(guān)聯(lián)上;往往這樣的限制條件能將關(guān)聯(lián)化繁為簡,既可以將“多對多”簡化為“一對多”,或?qū)ⅰ耙粚Χ唷焙喕癁椤耙粚σ弧?#xff1b;

實體(Entity)

? 實體就是領(lǐng)域模型中需要唯一標(biāo)示的領(lǐng)域概念。因為我們有時需要區(qū)分是那個實體。有兩個實體,如果唯一標(biāo)識不一樣,那么即便實體的其他所有屬性都一樣,我們也認(rèn)為他們是兩個不同的實體;因為實體有生命周期,實體從被創(chuàng)建后可能會被持久化到數(shù)據(jù)庫,然后有個時候又會被取出來。所以,如果我們不為實體定義一種可以唯一區(qū)分的標(biāo)識,那我們就無法區(qū)分到底是這個實體還是那個實體。另外,不應(yīng)該給實體定義太多的屬性和行為,而應(yīng)該需找關(guān)聯(lián),發(fā)現(xiàn)其他一些實體或值對象,將屬性或行為轉(zhuǎn)移到其他關(guān)聯(lián)的實體或值對象上。比如Csutomer視圖,他有一些地址信息,有地址信息是一個完整的有業(yè)務(wù)含義的概念。所以,我們可以定義一個Address對象,然后把Customer的地址相關(guān)的信息轉(zhuǎn)移到Address對象上,如果沒有Address對象,而把這些地址信息直接放在Customer對象上,并且如果對于一些其他的類似Address的信息也都直接放在Customer上,會導(dǎo)致Customer對象很混亂、結(jié)構(gòu)不清晰,最終導(dǎo)致他難以維護(hù)和理解,也就是我們要注意責(zé)任分配原則。

值對象(Value Object)

?在領(lǐng)域中,并不是每一個事物都必須有一個唯一標(biāo)示,也就是說關(guān)心對象是那個,而只關(guān)心對象是什么。就可以上面的地址對象Address為例,如果有兩個Customer的地址信息是一樣的,我們會認(rèn)為這兩個Cusomer的地址也是同一個。也就是說只要地址信息一樣,我們就認(rèn)為是同一個地址。用程序的方式來表達(dá)就是,如果兩個對象的所有的屬性的值都相同。我們會認(rèn)為他們是同一個對象的話,那么我們就可以把這個對象設(shè)計成值對象。因此,值對象沒有唯一標(biāo)識,這是和實體的最大不同,另外值對象在判斷是否是同一個對象時是通過他們的所有屬性是否相同,如果相同則認(rèn)為是同一個值對象了讓我們區(qū)分是否是同一個實體時,只看實體的唯一標(biāo)識是否相同,而不管實體的屬性是否相同;值對象另外一個明顯的特征是不可變,即所有的屬性都是只讀的。因為屬性是只讀的,所以可以被安全的共享;當(dāng)共享值對象時,一般有復(fù)制和共享兩種做法,具體采用哪種做法還要根據(jù)實際情況而定;另外,我們應(yīng)該給值對象設(shè)計的盡量簡單,不要讓它引用很多其他的對象,因為他只是一個值,就像int a=3;那么3就是一個傳統(tǒng)意義上說的值,而值對象其實也可以和這里的“3”一樣理解,也就是一個值,只不過是用對象來表示。所以,當(dāng)我們在C#語言中比較兩個值對象是否相等時,會重寫GetHashCode和Equals兩個方法,目的就是比較對象值;值對象雖然是只讀的,但是可以被整個替換掉。就像你把a的值修改為“a”(a=4;)一樣,直接把”3”這個值替換為”4”了。值對象也是一樣,當(dāng)你要修改Customer的Address對象引用時,不是通過Customer.Address.Street這樣的方式來實現(xiàn),因為值對象是只讀的,它是一個完整的不可分割的整體。我們可以這樣做:Customer.Address = new Address(…);

領(lǐng)域服務(wù)(Domain Service)

? 領(lǐng)域中的一些概念不太適合建模對象,即歸類到實體對象或值對象,因為他們本質(zhì)上就是一些操作,一些動作,而不是事物。這些操作或動作往往會設(shè)計多個領(lǐng)域?qū)ο?#xff0c;而且需要協(xié)調(diào)這些領(lǐng)域?qū)ο蠊餐瓿蛇@個操作或動作。如果強行將這些操作職責(zé)分配給任何一個對象,則被分配的對象就是承擔(dān)一些不該承擔(dān)的職責(zé),從而導(dǎo)致對象的職責(zé)不明很混亂。但是基于類的面向?qū)ο笳Z言規(guī)定任何屬性或行為都必須放在對象里面。所以我們需要需找一種新的模式來表示這種多個對象的操作,DDD認(rèn)為服務(wù)是一個很自然的范式用來對應(yīng)這種多個對象的操作,所以就有了領(lǐng)域服務(wù)這個模式,DDD認(rèn)為服務(wù)是一個很自然的范式用來對應(yīng)的這種跨多個對象的操作,所以就有了領(lǐng)域服務(wù)這個模式。和領(lǐng)域?qū)ο蟛煌?#xff0c;領(lǐng)域服務(wù)時以動詞開始來命名的。比如資金轉(zhuǎn)賬服務(wù)可以命名為MoneyTransferService。當(dāng)然,你可以吧服務(wù)理解為一個對象,但這和一般意義的對象有些區(qū)別。因為一般的領(lǐng)域?qū)ο蠖际怯袪顟B(tài)和行為的,而領(lǐng)域服務(wù)沒有狀態(tài)只有行為。需要強調(diào)的是領(lǐng)域服務(wù)的無狀態(tài)的,它存在的意義就是協(xié)調(diào)領(lǐng)域?qū)ο蠊餐瓿赡硞€操作,所有的狀態(tài)還是都保存在相應(yīng)的領(lǐng)域?qū)ο笾小N矣X得模型(實體)和服務(wù)(場景)是對領(lǐng)域的一種劃分,模型管住領(lǐng)域的個體行為,場景關(guān)注的是領(lǐng)域的群體行為,模型關(guān)注領(lǐng)域的靜態(tài)結(jié)構(gòu),場景關(guān)注領(lǐng)域的動態(tài)功能。這也符合了現(xiàn)實中出現(xiàn)的各種現(xiàn)象,有動有靜,有獨立有協(xié)作。

? 領(lǐng)域服務(wù)還有一個很重要的功能就是可以避免領(lǐng)域邏輯泄露到應(yīng)用層。因為如果沒有領(lǐng)域服務(wù),那么應(yīng)用層會直接調(diào)用領(lǐng)域?qū)ο笸瓿杀緫?yīng)該是屬于領(lǐng)域服務(wù)該做的操作,這樣一來,領(lǐng)域?qū)涌赡軙岩徊糠诸I(lǐng)域知識泄露到應(yīng)用層。因為應(yīng)用層需要了解每個領(lǐng)域?qū)ο蟮臉I(yè)務(wù)功能,具有哪些信息,以及它可鞥會有哪些其他領(lǐng)域?qū)ο蠼换?#xff0c;怎么交互等一系列領(lǐng)域知識。因此,引入領(lǐng)域服務(wù)可以有效的防止領(lǐng)域?qū)拥倪壿嬓孤兜綉?yīng)用層。對于應(yīng)用層來說,從可理解的角度來講,通過調(diào)用領(lǐng)域服務(wù)提供的簡單的但有意義的接口肯定也要比直接操作領(lǐng)域?qū)ο笕菀椎亩?#xff0c;這里似乎也看到了領(lǐng)域服務(wù)具有Facade的功能。還有一點就是易于測試。

說到領(lǐng)域服務(wù),還需要提一下軟件中的一般有三種服務(wù):應(yīng)用層服務(wù)、領(lǐng)域服務(wù)、基礎(chǔ)服務(wù)。

比如應(yīng)用層有一個資金轉(zhuǎn)賬的服務(wù),該服務(wù)主要做一下事情:

1、獲取輸入(如一個XML請求)

2、發(fā)送信息給領(lǐng)域?qū)臃?wù),要求其實現(xiàn)轉(zhuǎn)賬的業(yè)務(wù)邏輯

3、領(lǐng)域?qū)臃?wù)處理成功,則調(diào)用基礎(chǔ)服務(wù)層發(fā)送Email通知

領(lǐng)域?qū)拥姆?wù)做一下事情:

1、獲取賬號和目標(biāo)賬號,分別通知源賬號和目標(biāo)賬號進(jìn)行扣除資金和增加金額的操縱,當(dāng)然這里涉及到事務(wù)性操作

2、提供返回結(jié)果給應(yīng)用層

基礎(chǔ)層做一下事情:

1、按照應(yīng)用層的請求,發(fā)送Emalis通知;

所以,從上面的例子中可以清晰的看出,每種服務(wù)的職責(zé)。

聚合及聚合根(Aggregate,Aggregate Root)

? 聚合,它通過定義對象之間清晰的所屬關(guān)系和邊界來實現(xiàn)領(lǐng)域邏輯的內(nèi)聚,并避免了錯綜復(fù)雜的難以維護(hù)的對象關(guān)系網(wǎng)的形成。聚合定義了一組具有內(nèi)聚關(guān)系的相關(guān)對象的集合,我們把聚合看作是一個修改數(shù)據(jù)的單元。

聚合有以下一些特點:

1、每個聚合有一個根和一個邊界,邊界定義了聚合內(nèi)部的那些實體或者值對象,根是聚合內(nèi)的某個實體。

2、聚合內(nèi)部的對象之間可以互相引用,但是聚合外部如果要訪問聚合內(nèi)部的對象時,必須通過聚合根開始導(dǎo)航,絕對不能繞過聚合根直接訪問聚合內(nèi)的對象,也就是說聚合根是外部可以保持對他引用的唯一元素。

3、聚合內(nèi)除根以外的其他實體的唯一標(biāo)識都是本地標(biāo)識,也就是說只要在聚合內(nèi)部保持唯一即可,因為他們屬于這個聚合的;

4、聚合根負(fù)責(zé)與外部其他對象打交道并維護(hù)自己內(nèi)部的業(yè)務(wù)規(guī)則。

5、基于聚合的以上概念,我們可以推論出從數(shù)據(jù)庫查詢時單元也是以聚合為一個單元,也就是說我們不能直接查詢聚合內(nèi)部的某個非根的對象。

6、聚合內(nèi)部的對象可以保持其他聚合根的引用

7、刪除一個聚合根必須同時刪除聚合內(nèi)的所有相關(guān)對象,因為他們都屬于一個聚合,是一個完整的概念。

? 關(guān)于如何識別聚合以及聚合根的問題:

我覺得我們可以從業(yè)務(wù)的角度深入思考,然后慢慢分析出那些對象時:

1、有獨立存在意義的,即它是不依賴其他對象存在它才喲意義的

2、可以獨立訪問的,還是必須通過某個其他導(dǎo)航得到的

如何識別聚合?
我覺得這跟需要從業(yè)務(wù)的角度深入分析那些對象他們的關(guān)系是內(nèi)聚的,即我們會把他們看成一個整體來考慮的;然后這些對象我們就可以把他們放在一個聚合內(nèi)。所謂關(guān)系是內(nèi)聚的,是指這些對象必須保持一個固定規(guī)則,固定規(guī)則是指數(shù)據(jù)變化時必須保持不變的一致性規(guī)則。當(dāng)我們在修改一個聚合時,我們必須在事務(wù)級別確保整個聚合內(nèi)的所有對象滿足這個固定規(guī)則。作為一條建議,聚合盡量不要太大,否則即便嫩歐股做到事務(wù)級別保持聚合的業(yè)務(wù)規(guī)則完整性,也可能會帶來一定的性能問題。有分析報告顯示,通常在大部分領(lǐng)域模型中,有70%的聚合通常只有一個實體,即聚合根,該實體內(nèi)沒有包含其他實體,只包含一些值對象;另外30%的聚合中,基本上也只包含兩到三個實體。這因為這大部分的聚合都只是一個實體,該實體也就是聚合根了。

如何識別聚合根?

如果一個聚合只有一個實體,那么這個實體就是聚合根;如果有多個實體,那么我們可以思考聚合內(nèi)那個對象有獨立存在的意義并且可以和外部直接進(jìn)行交互。

工廠(Factory)

?? DDD的工廠也是一種體現(xiàn)封裝思想的模式,DDD中引入工廠模式的原因是:有時創(chuàng)建一個領(lǐng)域?qū)ο笫且患容^復(fù)雜的事情,不僅僅是簡單的New操作。正如對象封裝了內(nèi)部實現(xiàn)一樣(我們無需要知道對象內(nèi)部創(chuàng)建實現(xiàn)就可以使用對象的行為),工廠則是用來封裝創(chuàng)建一個復(fù)雜對象尤其是聚合時所需要的知識,工廠的作用是將創(chuàng)建對象的細(xì)節(jié)隱藏起來。客戶傳遞給工廠一些簡單的參數(shù),然后工廠可以在內(nèi)部創(chuàng)建出一個復(fù)雜的領(lǐng)域?qū)ο笕缓蠓祷亟o客戶。領(lǐng)域模型中其他元素都不適合做這個事情,所以就引入了這個新的模式——工廠。工廠在創(chuàng)建一個復(fù)雜領(lǐng)域?qū)ο髸r,通常會知道該滿足什么業(yè)務(wù)規(guī)則(它知道先怎樣實例化一個對象,然后在對這個對象做哪些初始化操作,這些知識就是創(chuàng)建對象的細(xì)節(jié)),如果傳遞進(jìn)來的參數(shù)符合創(chuàng)建對象的業(yè)務(wù)規(guī)則,則可以順利創(chuàng)建相應(yīng)的對象;但是如果由于參數(shù)無效等原因不能創(chuàng)建出期望的對象時,應(yīng)該拋出一個異常,以確保不會創(chuàng)建一個錯誤的對象。當(dāng)然我們也并不總是需要通過工廠來創(chuàng)建對象,事實上大部分情況下領(lǐng)域?qū)ο蟮膭?chuàng)建都不會太復(fù)雜,所以我們只需要簡單的使用構(gòu)造函數(shù)創(chuàng)建對象就可以了。

? 隱藏創(chuàng)建對象的好處是顯而易見的,這樣可以不會讓領(lǐng)域?qū)拥臉I(yè)務(wù)邏輯泄露到應(yīng)用層,同時也減輕了應(yīng)用層的負(fù)擔(dān),它只需要簡單的調(diào)用領(lǐng)域工廠創(chuàng)建出期望的對象即可。

倉儲(Repostistory)

1、倉儲被設(shè)計出來的目的是基于這個原因:領(lǐng)域模型中的對象自從被創(chuàng)建出來后不會一直留在內(nèi)存中活動的,當(dāng)它不活動時被持久化到數(shù)據(jù)庫中,然后當(dāng)需要的時候我們會重建該對象;重建對象就是根據(jù)數(shù)據(jù)庫中已存在的對象的狀態(tài)重新創(chuàng)建對象的過程;所以,可見重建對象是一個和數(shù)據(jù)庫打交道的過程。從更廣義的角度來理解,我們經(jīng)常會像集合一樣從某個類似集合的地方根據(jù)某個條件獲取一個或一些對象,往集合中添加對象和移除對象。創(chuàng)建就是基于這樣的思路被設(shè)計出來的。

2、倉儲里面存放的對象一定是聚合,原因是之前提到的領(lǐng)域模型中是以聚合的概念去劃分邊界的;聚合是我們更新對象的一個邊界,事實上我們把整個聚合看成一個整體概念,要么一起被取出來,要么一起被刪除。我們永遠(yuǎn)不會單獨對某個聚合內(nèi)的子對象進(jìn)行單獨查詢或做更新操作。因此,我們只對聚合設(shè)計倉儲。

3、倉儲還有一個重要的特征就是分為存儲定義部分和倉儲實現(xiàn)部分,在領(lǐng)域模型中我們定義倉儲的接口,而在基礎(chǔ)設(shè)施才實現(xiàn)具體的倉儲。這樣做的原因是:由于倉儲背后的實現(xiàn)都是在和數(shù)據(jù)庫打交道,但是我們又不希望客戶(如應(yīng)用層)把重點放在如何從數(shù)據(jù)庫獲取數(shù)據(jù)的問題上,因為這樣做會導(dǎo)致客戶(應(yīng)用層)代碼很混亂,很可能會因此而忽略了領(lǐng)域模型的存在。所以我們需要提供一個簡單明了的接口,供客戶使用,確保客戶能以最簡單的方式獲取領(lǐng)域?qū)ο?#xff0c;從而可以讓它專心的不會被什么數(shù)據(jù)訪問代碼打擾的情況下協(xié)調(diào)領(lǐng)域?qū)ο笸瓿蛇壿嫛_@種通過接口來隔離封裝變化的做法其實很常見。由于客戶面對的是抽象的接口并不是具體的實現(xiàn),所以我們可以隨時替換倉儲的真實實現(xiàn),這很有助于我們做單元測試。

4、盡管倉儲可以像集合一樣在內(nèi)存中管理對象,但是倉儲一般不負(fù)責(zé)事務(wù)處理。一般事務(wù)處理會交給一個叫“工作單元(unit of work)”的東西。這里不準(zhǔn)備詳細(xì)討論工作單元的工作原理。

5、另外,倉儲在設(shè)計查詢接口時,可能還會用到規(guī)格模式(Specification Pattern),我見過的最厲害的規(guī)格模式應(yīng)該就是Linq以及Dlinq查詢了。一般我們會根據(jù)項目中查詢的靈活度要求來選擇適合的倉儲查詢接口設(shè)計。通常情況下只需要定義簡單明了的具有固定查詢參數(shù)接口就可以了。只有是在查詢條件式動態(tài)指定的情況下才可能需要用到Specification等模式。

設(shè)計領(lǐng)域模型的一般步驟

1、根據(jù)需求建立一個初步的領(lǐng)域模型,識別出一些明顯的領(lǐng)域概念以及他們的關(guān)聯(lián),關(guān)聯(lián)可以暫時沒有方向但需要有(1:1,1:N,M:N)這些關(guān)系;可以用文字精確的沒有歧義的描述每個領(lǐng)域概念的涵義以及包含的主要信息

2、分析主要的軟件應(yīng)用程序功能,識別出主要的應(yīng)用層的類;這樣有助于及早發(fā)現(xiàn)那些應(yīng)用層的職責(zé),那些是領(lǐng)域?qū)拥穆氊?zé);

3、進(jìn)一步分析領(lǐng)域模型,識別出那些是實體,那些是值對象,那些是領(lǐng)域服務(wù);

4、分析關(guān)聯(lián),通過對業(yè)務(wù)的更深入分析以及各種設(shè)計原則以及性能方面的權(quán)衡,明確關(guān)聯(lián)的方向或者去掉一些不需要的關(guān)聯(lián)

5、找出聚合邊界以及聚合根,這是一件很有難度事情,因為你在分析的過程中往往會碰到很多領(lǐng)域兩可的清晰判斷的選擇問題,所以,需要我們平時一些分析經(jīng)驗的積累才能找出正確的聚合根;

6、為聚合根配備倉儲,一般情況下是為了一個聚合分配一個倉儲,此時只要在設(shè)計好倉儲的接口即可

7、走查場景,確定我們設(shè)計的領(lǐng)域模型能夠有效的解決業(yè)務(wù)需求

8、考慮如何創(chuàng)建領(lǐng)域?qū)嶓w或值對象,是通過工廠還是直接通過構(gòu)造函數(shù)

9、停下來重構(gòu)模型。尋找模型中覺得有些疑問或者是蹩腳的地方,比如思考一些對象應(yīng)該通過關(guān)聯(lián)導(dǎo)航得到還是應(yīng)該從倉儲獲取?聚合設(shè)計的是否正確?考慮模型的性能怎樣..等等。

領(lǐng)域模型是一個不斷重構(gòu),持續(xù)完善模型的過程,大家會在討論中將變化的部分反映到模型中,從而使模型不斷細(xì)化并朝著正確的方向走。領(lǐng)域建模時領(lǐng)域?qū)<摇⒃O(shè)計人員、開發(fā)人員之間溝通交流的過程,是大家工作和思考問題的基礎(chǔ)。

領(lǐng)域驅(qū)動設(shè)計的其他一些主題

上面只是涉及到DDD中最基本的內(nèi)容,DDD中還有很多其他重要的內(nèi)容是上面沒提到的,如:

1、模型上下文、上下文映射、上下文共享

2、如何將分析模式和設(shè)計模式運用到DDD中

3、一些關(guān)于柔性設(shè)計的技巧

4、如果保持模型的完整性,以及持續(xù)集成方面的知識

5、如何精煉模型,識別核心模型以及通用子領(lǐng)域

6....

這些主題都很重要,因為篇幅有限以及我目前掌握的知識也有限,并且為了突出這篇文章的重點,所以不對這些驚醒星系介紹。

一些相關(guān)的擴展閱讀

?1、CQRS架構(gòu)

核心思想是將應(yīng)用程序的查詢部分和命令部分完全分離,這兩部分可以完全不同的模型和技術(shù)去實現(xiàn),比如命令部分可以通過領(lǐng)域驅(qū)動設(shè)計來實現(xiàn);查詢部分可以直接用最快的非面向?qū)ο蟮姆绞饺崿F(xiàn),比如用SQL。這樣的思想很多的好處:

1、實現(xiàn)命令部分的領(lǐng)域模型不用經(jīng)常為了領(lǐng)域?qū)ο罂赡軙蝗绾尾樵兌鲆恍┱壑刑幚?/p>

2、由于命令和查詢時完全分離的,所以這兩部分可以用不同的技術(shù)架構(gòu)去實現(xiàn),包括數(shù)據(jù)庫設(shè)計都可以分開設(shè)計,每一部分可以充分發(fā)揮其長處

3、高性能、命令端因為沒有返回值,可以像消息隊列一樣接受命令,放在隊列中,慢慢處理,處理完后,可以通過異步的方式通知查詢端,這樣查詢端可以做到數(shù)據(jù)同步的處理。

Event Sourcing

基于DDD的設(shè)計,對于聚合,不保存聚合的當(dāng)前狀態(tài),而是保存對象上所發(fā)生的每個事件。當(dāng)要重建一個聚合對象時,可以通過回溯這些事件(即讓這些事件重新發(fā)生)來讓對象恢復(fù)到某個特定的狀態(tài);因為有時一個聚合可能會發(fā)生很多事件,所以如果每次要重建對象時從夠回溯事件,會導(dǎo)致性能低下,所以我們會在一定時候為創(chuàng)建一個快照。這樣,我們就可以基于某個快照開始創(chuàng)建聚合對象了。

DCI架構(gòu)

?DCI架構(gòu)師強調(diào),軟件應(yīng)該真實的模擬現(xiàn)實生活中對象的交互方式,代碼應(yīng)該準(zhǔn)確樸實的放映用戶的心智模型。在DCI中有:數(shù)據(jù)模型、角色模型、以及上下文這三個概念。數(shù)據(jù)模型表示程序的結(jié)構(gòu),目前我們所理解的DDD中的領(lǐng)域模型可以很好的表示數(shù)據(jù)模型;角色模型表示數(shù)據(jù)如何交互,一個角色定義了某個“身份”所具有的交互行為;上下文對應(yīng)業(yè)務(wù)場景,用于實現(xiàn)業(yè)務(wù)用例,注意是業(yè)務(wù)用例而不是系統(tǒng)用例,業(yè)務(wù)用例只與業(yè)務(wù)相關(guān);軟件運行時,根據(jù)用戶的操作,系統(tǒng)創(chuàng)建相應(yīng)的場景,并把相關(guān)的數(shù)據(jù)對象作為場景參與者傳遞給場景,然后場景知道該為每個對象賦予什么角色,當(dāng)對象被賦予某個角色后就真正成為有交互能力的對象,然后與其他對象進(jìn)行交互;這個過程與現(xiàn)實生活中我們所理解的對象是一致的;

  DCI的這種思想與DDD中的領(lǐng)域服務(wù)所做的事情是一樣的,但實現(xiàn)的角度有些不同。DDD中的領(lǐng)域服務(wù)被創(chuàng)建的出發(fā)點是當(dāng)一些職責(zé)不太適合放在任何一個領(lǐng)域?qū)ο笊蠒r,這個職責(zé)往往對應(yīng)領(lǐng)域中的某個活動或轉(zhuǎn)換過程,此時我們應(yīng)該考慮將其放在一個服務(wù)中。比如資金轉(zhuǎn)帳的例子,我們應(yīng)該提供一個資金轉(zhuǎn)帳的服務(wù),用來對應(yīng)領(lǐng)域中的資金轉(zhuǎn)帳這個領(lǐng)域概念。但是領(lǐng)域服務(wù)內(nèi)部做的事情是協(xié)調(diào)多個領(lǐng)域?qū)ο笸瓿梢患虑椤R虼?#xff0c;在DDD中的領(lǐng)域服務(wù)在協(xié)調(diào)領(lǐng)域?qū)ο笞鍪虑闀r,領(lǐng)域?qū)ο笸翘幱谝粋€被動的地位,領(lǐng)域服務(wù)通知每個對象要求其做自己能做的事情,這樣就行了。這個過程中我們似乎看不到對象之間交互的意思,因為整個過程都是由領(lǐng)域服務(wù)以面向過程的思維去實現(xiàn)了。而DCI則通用引入角色,賦予角色以交互能力,然后讓角色之間進(jìn)行交互,從而可以讓我們看到對象與對象之間交互的過程。但前提是,對象之間確實是在交互。因為現(xiàn)實生活中并不是所有的對象在做交互,比如有A、B、C三個對象,A通知B做事情,A通知C做事情,此時可以認(rèn)為A和B,A和C之間是在交互,但是B和C之間沒有交互。所以我們需要分清這種情況。資金轉(zhuǎn)帳的例子,A相當(dāng)于轉(zhuǎn)帳服務(wù),B相當(dāng)于帳號1,C相當(dāng)于帳號2。因此,資金轉(zhuǎn)帳這個業(yè)務(wù)場景,用領(lǐng)域服務(wù)比較自然。有人認(rèn)為DCI可以替換DDD中的領(lǐng)域服務(wù),我持懷疑態(tài)度。

  四色原型分析模式:

  1) 時刻-時間段原型(Moment-Interval Archetype)

  表示在某個時刻或某一段時間內(nèi)發(fā)生的某個活動。使用粉紅色表示,簡寫為MI。

  2) 參與方-地點-物品原型(Part-Place-Thing Archetype)

  表示參與某個活動的人或物,地點則是活動的發(fā)生地。使用綠色表示。簡寫為PPT。

  3) 描述原型(Description Archetype)

  表示對PPT的本質(zhì)描述。它不是PPT的分類!Description是從PPT抽象出來的不變的共性的屬性的集合。使用藍(lán)色表示,簡寫為DESC。

  舉個例子,有一個人叫張三,如果某個外星人問你張三是什么?你會怎么說?可能會說,張三是個人,但是外星人不知道“人”是什么。然后你會怎么辦?你就會說:張三是個由一個頭、兩只手、兩只腳,以及一個身體組成的客觀存在。雖然這時外星人仍然不知道人是什么,但我已經(jīng)可以借用這個例子向大家說明什么是“Description”了。在這個例子中,張三就是一個PPT,而“由一個頭、兩只手、兩只腳,以及一個身體組成的客觀存在”就是對張三的Description,頭、手、腳、身體則是人的本質(zhì)的不變的共性的屬性的集合。但我們?nèi)祟惐容^聰明,很會抽象總結(jié)和命名,已經(jīng)把這個Description用一個字來代替了,那就是“人”。所以就有所謂的張三是人的說法。

  4) 角色原型(Role Archetype)

  角色就是我們平時所理解的“身份”。使用黃色表示,簡寫為Role。為什么會有角色這個概念?因為有些活動,只允許具有特定角色(身份)的PPT(參與者)才能參與該活動。比如一個人只有具有教師的角色才能上課(一種活動);一個人只有是一個合法公民才能參與選舉和被選舉;但是有些活動也是不需要角色的,比如一個人不需要具備任何角色就可以睡覺(一種活動)。當(dāng)然,其實說人不需要角色就能睡覺也是錯誤的,錯在哪里?因為我們可以這樣理解:一個客觀存在只要具有“人”的角色就能睡覺,其實這時候,我們已經(jīng)把DESC當(dāng)作角色來看待了。所以,其實角色這個概念是非常廣的,不能用我們平時所理解的狹義的“身份”來理解,因為“教師”、“合法公民”、“人”都可以被作為角色來看待。因此,應(yīng)該這樣說:任何一個活動,都需要具有一定角色的參與者才能參與。

  用一句話來概括四色原型就是:一個什么什么樣的人或組織或物品以某種角色在某個時刻或某段時間內(nèi)參與某個活動。 其中“什么什么樣的”就是DESC,“人或組織或物品”就是PPT,“角色”就是Role,而”某個時刻或某段時間內(nèi)的某個活動"就是MI。

  以上這些東西如果在學(xué)習(xí)了DDD之后再去學(xué)習(xí)會對DDD有更深入的了解,但我覺得DDD相對比較基礎(chǔ),如果我們在已經(jīng)了解了DDD的基礎(chǔ)之上再去學(xué)習(xí)這些東西會更加有效和容易掌握。?

?

轉(zhuǎn)載于:https://www.cnblogs.com/zhijianliutang/archive/2012/03/08/2384961.html

總結(jié)

以上是生活随笔為你收集整理的领域设计基本理论知识总结(转)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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