三層架構(gòu)與DDD

1.從傳統(tǒng)三層架構(gòu)與DDD分層架構(gòu)的編程演變其實是思想的演變锄俄。

傳統(tǒng)三層架構(gòu)源譬,即用戶界面層UI诅诱、業(yè)務(wù)邏輯層BAL千贯、數(shù)據(jù)訪問層DAL屯仗。一般同時還有建立一個Model實體類的工程項目。DDD分層架構(gòu)搔谴,即表現(xiàn)層UI祭钉、應(yīng)用層Application、領(lǐng)域驅(qū)動層Doman己沛、基礎(chǔ)設(shè)施層Infrastructure慌核。


傳統(tǒng)三層架構(gòu)距境,我一直使用、結(jié)構(gòu)單一垮卓、邏輯也清晰垫桂,三層各處理各自的事務(wù),上層向下層引用接口與方法粟按,下層向上層提供接口服務(wù)诬滩,各層之間調(diào)度方法時可能通過Model傳值,也可以返回值Model灭将。但以往疼鸟,我處理的業(yè)務(wù)邏輯層中,基本上都是將DAL層的接口值返回給業(yè)務(wù)邏輯層庙曙,然后BLL層再將結(jié)果返回給UI層空镜,BLL層只做了上傳下達(dá)的作用,其它的作用發(fā)揮得較少捌朴。以往三層架構(gòu)中的重點是BLL層吴攒。當(dāng)我需要新增業(yè)務(wù)功能時,或者需要CRUD操作砂蔽,需要將UI層洼怔、BLL層、DAL層都需要增加類文件以達(dá)到處理CRUD操作的功能左驾。當(dāng)然镣隶,傳統(tǒng)三層架構(gòu)中,也會引入一個新的Common工程或Utility工程诡右,為BLL矾缓、DAL、UI提供共用或通用方法或行為的支持稻爬。若是有需要對第三方軟件或系統(tǒng)提供數(shù)據(jù)接口嗜闻,這時,可以將接口作為IIS站點或WebService 或WebAPI提供桅锄,此時這個接口可以放在UI供第三方調(diào)用琉雳。

DDD分層架構(gòu),是從傳三層架構(gòu)中演變過來的友瘤。它將傳統(tǒng)的三層架構(gòu)做了一定的變更翠肘,將四個層中的內(nèi)容做了重新歸內(nèi),并對分層結(jié)構(gòu)的業(yè)務(wù)重點作了分配辫秧。UI層還是UI層束倍、應(yīng)用層用于調(diào)度第三方的應(yīng)用接口、以及提供口服務(wù)給第三方,同時將在應(yīng)用層增加Dto工程項目用于操作應(yīng)用層與UI層的數(shù)據(jù)傳遞即值對象傳遞以及Dto與Model實體類之間的映射绪妹,將傳統(tǒng)的BLL層甥桂、Model層歸納到領(lǐng)域驅(qū)動層Domain中,同時將倉儲的接口層放在Domain層中邮旷,將傳統(tǒng)的DAL層實現(xiàn)以及通用的Common層或Utility層歸納到基礎(chǔ)設(shè)施層Infrastructure中黄选。


從傳統(tǒng)三層架構(gòu)(包括Common公共層、Model實體層)演變到DDD領(lǐng)域驅(qū)動模型設(shè)計的分層架構(gòu)婶肩,從項目歸納上比較办陷,可能多了DAL接口層即倉儲接口層,其它的工程項目只是做了位置上的遷移律歼。同時傳統(tǒng)BLL層的命名變理為Service民镜,同時在應(yīng)用層增加了Dto工程項目。

2.通過例子說明DDD相對于三層架構(gòu)的好處:

例子:?假設(shè)現(xiàn)在有一個銀行支付系統(tǒng)項目险毁,其中的一個重要的業(yè)務(wù)用例是賬戶轉(zhuǎn)賬業(yè)務(wù)制圈。系統(tǒng)使用迭代的方式進(jìn)行開發(fā),在1.0版本中辱揭,該用例的功能需求非常簡單,事件流描述如下:?

主事件流:?

1) 用戶登錄銀行的在線支付系統(tǒng)?

2) 選擇用戶在該銀行注冊的網(wǎng)上銀行賬戶?

3) 選擇需要轉(zhuǎn)賬的目標(biāo)賬戶病附,輸入轉(zhuǎn)賬金額问窃,申請轉(zhuǎn)賬?

4) 銀行系統(tǒng)檢查轉(zhuǎn)出賬戶的金額是否足夠?

5) 從轉(zhuǎn)出賬戶中扣除轉(zhuǎn)出金額(debit),更新轉(zhuǎn)出賬戶的余額?

6) 把轉(zhuǎn)出金額加入到轉(zhuǎn)入賬戶中(credit)完沪,更新轉(zhuǎn)入賬戶的余額?

備選事件流:?

4a)如果轉(zhuǎn)出賬戶中的余額不足域庇,轉(zhuǎn)賬失敗,返回錯誤信息

A覆积、面向過程的設(shè)計方式(貧血模型)

設(shè)計方案如下(忽略展示層部分)[V1.0.0]:?


1) 設(shè)計一個賬戶交易服務(wù)接口AccountingService听皿,設(shè)計一個服務(wù)方法transfer(),并提供一個具體實現(xiàn)類AccountingServiceImpl宽档,所有賬戶交易業(yè)務(wù)的業(yè)務(wù)邏輯都置于該服務(wù)類中尉姨。?


2) 提供一個AccountInfo和一個Account,前者是一個用于與展示層交換賬戶數(shù)據(jù)的賬戶數(shù)據(jù)傳輸對象吗冤,后者是一個賬戶實體(相當(dāng)于一個EntityBean)又厉,這兩個對象都是普通的JavaBean,具有相關(guān)屬性和簡單的get/set方法椎瘟。?


這時候覆致,新需求來了,在1.0.1版本中肺蔚,需要為賬戶轉(zhuǎn)賬業(yè)務(wù)增加如下功能煌妈,在轉(zhuǎn)賬時,首先需要判斷賬戶是否可用,然后璧诵,賬戶的余額還要分成兩部分:凍結(jié)部分和活躍部分汰蜘,處于凍結(jié)部分的金額不能用于任何交易業(yè)務(wù),我們來看看變更后的代碼?[V1.0.1]


這時候腮猖,1.0.2的需求又來了鉴扫,需要在每次交易成功后,創(chuàng)建一個交易明細(xì)賬澈缺,于是坪创,我們又必須在transfer()方面里面增加創(chuàng)建并持久化交易明細(xì)賬的業(yè)務(wù)邏輯?[V1.0.2]?:

  業(yè)務(wù)需求不斷復(fù)雜化:賬戶每筆轉(zhuǎn)賬的最大額度需要由其信用指數(shù)確定、需要根據(jù)銀行的手續(xù)費策略計算并扣除一定的手續(xù)費用……姐赡,隨著業(yè)務(wù)的復(fù)雜化莱预,transfer()方法的邏輯變得越來越復(fù)雜,逐漸形成了上文所述的成百上千行代碼项滑。有經(jīng)驗的程序員可能會做出類此“方法抽取”的重構(gòu)依沮,把轉(zhuǎn)賬業(yè)務(wù)按邏輯劃分成若干塊:判斷余額是否足夠、判斷賬戶的信用指數(shù)以確定每筆最大轉(zhuǎn)賬金額枪狂、根據(jù)銀行的手續(xù)費策略計算手續(xù)費危喉、記錄交易明細(xì)賬……,從而使代碼更加結(jié)構(gòu)化州疾。這是一個好的開始辜限,但還是顯然不足。

假設(shè)某一天严蓖,系統(tǒng)需求增加一個新的模塊薄嫡,為系統(tǒng)增加一個網(wǎng)上商城,讓銀行用戶可以進(jìn)行在線購物颗胡,而在線購物也存在著很多與賬戶貸記借記業(yè)務(wù)相同或相似的業(yè)務(wù)邏輯:判斷余額是否足夠毫深、對賬戶進(jìn)行借貸操作(credit/debit)以改變余額、收取手續(xù)費用毒姨、產(chǎn)生交易明細(xì)賬……?


面對這種情況哑蔫,有兩種解決辦法?[V1.0.3]?:

1) 把AccountingServiceImpl中的相同邏輯拷貝到OnlineShoppingServiceImplementation中;

2) 讓OnlineShoppingServiceImpl調(diào)用AccountingServiceImpl的相同服務(wù).?


顯然,第二種方法比第一種方法更好弧呐,結(jié)構(gòu)更清晰鸳址,維護(hù)更容易。但問題在于泉懦,這樣就會形成網(wǎng)上商城服務(wù)模塊與賬戶收支服務(wù)模塊的不必要的依賴關(guān)系稿黍,系統(tǒng)的耦合度高了,如果系統(tǒng)為了更靈活的伸縮性崩哩,讓每個大業(yè)務(wù)模塊獨立進(jìn)行部署巡球,還需要因為兩者的依賴關(guān)系建立分布式調(diào)用言沐,這無疑增加了設(shè)計、開發(fā)和運維的成本酣栈。?


有經(jīng)驗的設(shè)計人員可能會發(fā)現(xiàn)第三種解決辦法:把相同的業(yè)務(wù)邏輯抽取成一個新的服務(wù)险胰,作為公共服務(wù)同時供上述兩個業(yè)務(wù)模塊使用。這就是筆者將會馬上討論的方案——使用領(lǐng)域驅(qū)動設(shè)計矿筝。

B.面向過程的領(lǐng)域驅(qū)動設(shè)計方式(充血模型)

為了節(jié)省篇幅起便,這里就直接以最復(fù)雜的業(yè)務(wù)需求來進(jìn)行設(shè)計。?


領(lǐng)域驅(qū)動設(shè)計的一個重要的概念是領(lǐng)域模型窖维,首先榆综,我們根據(jù)業(yè)務(wù)領(lǐng)域抽象出以下核心業(yè)務(wù)對象模型:?


Account:賬戶,是整個系統(tǒng)的最核心的業(yè)務(wù)對象铸史,它包括以下屬性:對象標(biāo)識鼻疮、賬戶號、是否有效標(biāo)識琳轿、余額判沟、凍結(jié)金額、賬戶交易明細(xì)集合崭篡、賬戶信用等級挪哄。?

AccountTransactionDetails:?賬戶交易明細(xì),它從屬于賬戶琉闪,每個賬戶有多個交易明細(xì)迹炼,它包括以下屬性:對象標(biāo)識、所屬賬戶塘偎、交易類型疗涉、交易發(fā)生金額拿霉、交易發(fā)生時間吟秩。?

AccountCreditDegree:?賬戶信用等級,它用于限制賬戶的每筆交易發(fā)生金額绽淘,包含以下屬性:對象標(biāo)識涵防、對應(yīng)賬戶、信用指數(shù)沪铭。?

BankTransactionFeeCalculator:?銀行交易手續(xù)費用計算器壮池,它包含一個常量:每筆交易的手續(xù)費上限。

我們知道杀怠,領(lǐng)域?qū)ο蟪司哂凶陨淼膶傩院蜖顟B(tài)之外椰憋,它的一個很重要的標(biāo)志是,它具有屬于自己職責(zé)范圍之內(nèi)的行為赔退,這些行為封裝了其領(lǐng)域內(nèi)的領(lǐng)域業(yè)務(wù)邏輯橙依。于是证舟,我們進(jìn)行進(jìn)一步的建模,根據(jù)業(yè)務(wù)需求為領(lǐng)域?qū)ο笤O(shè)計業(yè)務(wù)方法:?


  根據(jù)職責(zé)單一的原則窗骑,我們把功能需求中描述的功能合理的分配到不同的領(lǐng)域?qū)ο笾校?/p>

Account:?

credit:向銀行賬戶存入金額女责,貸記?

debit:從銀行賬戶劃出金額,借記?

transferTo:把固定金額轉(zhuǎn)入指定賬戶?

createTransactionDetails:創(chuàng)建交易明細(xì)賬?

updateCreditIndex:更新賬戶的信用指數(shù)?

(我們可以看到创译,后兩個業(yè)務(wù)方法被聲明為protected抵知,具體原因見后述)

AccountCreditDegree:?

getMaxTransactionAmount:獲取所屬賬戶的每筆交易最大金額

BankTransactionFeeCalculator:

  calculateTransactionFee:根據(jù)交易信息計算該筆交易的手續(xù)費

經(jīng)過這樣的設(shè)計,前例中所有放置在服務(wù)對象的業(yè)務(wù)邏輯被分別劃入不同的負(fù)責(zé)相關(guān)職責(zé)的領(lǐng)域?qū)ο螽?dāng)中软族,下面的時序圖描述了AccountingServiceImpl的轉(zhuǎn)賬業(yè)務(wù)的實現(xiàn)邏輯(為了簡化邏輯刷喜,我們忽略掉事物、持久化等邏輯):?


  再看看AccountingServiceImpl.transfer()的實現(xiàn)邏輯:


  我們可以看到互订,上例那些復(fù)雜的業(yè)務(wù)邏輯:判斷余額是否足夠吱肌、判斷賬戶是否可用、改變賬戶余額仰禽、計算手續(xù)費氮墨、判斷交易額度、產(chǎn)生交易明細(xì)賬……吐葵,都不再存在于AccountingServiceImplementation的transfer方法中规揪,它們被委派給負(fù)責(zé)這些業(yè)務(wù)的領(lǐng)域?qū)ο蟮臉I(yè)務(wù)方法中去,現(xiàn)在應(yīng)該猜到為什么Account中有兩個方法被聲明為protected了吧温峭,因為他們是在debit和credit方法被調(diào)用時猛铅,由這兩個方法調(diào)用的,對于AccountingServiceImpl來說凤藏,由于產(chǎn)生交易明細(xì)(createTransactionDetails)和更新賬戶信用指數(shù)(updateCreditIndex)都不屬于其職責(zé)范圍奸忽,它不需要也無權(quán)使用這些邏輯。

我們可以看到揖庄,使用領(lǐng)域驅(qū)動設(shè)計至少會帶來下述優(yōu)點:

業(yè)務(wù)邏輯被合理的分散到不同的領(lǐng)域?qū)ο笾欣醪耍a結(jié)構(gòu)更加清晰,可讀性蹄梢,可維護(hù)性更高疙筹;

對象職責(zé)更加單一,內(nèi)聚度更高禁炒;

復(fù)雜的業(yè)務(wù)模型可以通過領(lǐng)域建模(UML是一種主要方式)清晰的表達(dá)而咆,開發(fā)人員甚至可以在不讀源碼的情況下就能了解業(yè)務(wù)和系統(tǒng)結(jié)構(gòu),這有利于對現(xiàn)存的系統(tǒng)進(jìn)行維護(hù)和迭代開發(fā)幕袱;

  再看看如果這時需要加入網(wǎng)上商城的一個新的模塊暴备,開發(fā)人員需要怎么去做,還記得上面提過的第三種方案嗎们豌?就是把賬戶貸記和借記的相關(guān)業(yè)務(wù)抽取到成一個公共服務(wù)涯捻,同時供銀行在線支付系統(tǒng)和網(wǎng)上商城系統(tǒng)服務(wù)阁危,其實這個公共的服務(wù),本質(zhì)上就是這些具有領(lǐng)域邏輯的領(lǐng)域?qū)ο螅篈ccount汰瘫、AccountCreditDegree狂打、……,由此我們又可以發(fā)現(xiàn)領(lǐng)域驅(qū)動設(shè)計的一大優(yōu)點:

系統(tǒng)高度模塊化混弥,代碼重用度高趴乡,不會出現(xiàn)太多的重復(fù)邏輯。


3.DDD中分層架構(gòu)

在分解復(fù)雜的軟件系統(tǒng)時蝗拿,分層是我們最常用的手段之一晾捏。然而,在領(lǐng)域驅(qū)動設(shè)計中哀托,層次和包的劃分看起來與我們的結(jié)構(gòu)又有一定區(qū)別惦辛,本文主要討論DDD中的分層架構(gòu)及每層的意義,以及與傳統(tǒng)的三層架構(gòu)的區(qū)別仓手。

為什么要分層?

軟件設(shè)計中分層的設(shè)計隨處可見胖齐,但是分層能帶來什么好處呢?或者說嗽冒,我們?yōu)槭裁匆紤]分層架構(gòu)呢呀伙?

由于現(xiàn)實世界的復(fù)雜性,分層可以提供一個相對高層的視角來分解和簡化我們的問題添坊,此外分層也可帶來可測試剿另、可維護(hù)性、靈活性贬蛙、可擴展性等方面的好處雨女。

簡化復(fù)雜性,關(guān)注點分離阳准,結(jié)構(gòu)清晰氛堕;?

降低耦合度,隔離層次溺职,降低依賴(上層無需關(guān)注下層具體實現(xiàn))岔擂,利于分工位喂、測試和維護(hù)(可維護(hù)性)浪耘;?

提高靈活性,可以靈活替換某層的實現(xiàn)塑崖;?

提高擴展性七冲,方便實現(xiàn)分布式部署;?

看起來十分簡單规婆,好像就是把系統(tǒng)劃分為一定的層數(shù)澜躺,并把他們堆疊組織起來蝉稳。但是,當(dāng)落實到具體的實踐時掘鄙,如何劃分耘戚、各層存在的意義、如何取舍以及相應(yīng)的依賴關(guān)系卻并沒有想象中那么容易操漠,邊界的重合部分收津、不同場景下關(guān)注點、層次內(nèi)部的具體分解以及層次的粒度等都是我們需要考慮的問題浊伙。

什么是分層架構(gòu)?

2.1 分層的歷史?

最廣為人知的應(yīng)該就是經(jīng)典的三層架構(gòu):展示層撞秋、業(yè)務(wù)邏輯層、數(shù)據(jù)訪問層嚣鄙。

Martin Fowler在《企業(yè)應(yīng)用架構(gòu)模式》中也是類似的三層進(jìn)行展開的:表現(xiàn)層吻贿,領(lǐng)域?qū)樱瑪?shù)據(jù)源層哑子。

還有各種其他分層架構(gòu)舅列,這里就不一一描述了。

面對如此多的分層架構(gòu)卧蜓,我們不禁思考剧蹂,他們分層的依據(jù)又是什么?能否抽象出一些相同點和不同點烦却?又該在什么時候加入哪些合適的中間層宠叼?在實踐中我們又該采取怎樣的架構(gòu)呢?

2.2 分層的本質(zhì)?

分層其實是把一系列相同或相似的對象進(jìn)行分類放在同一層,然后根據(jù)他們之間的依賴關(guān)系再確定上下層次關(guān)系其爵∶岸可以看出,分層的核心在于分類和關(guān)聯(lián)摩渺。

通常简烤,我們可以將系統(tǒng)劃分為變化較大的業(yè)務(wù)部分和相對穩(wěn)定的技術(shù)部分;對于業(yè)務(wù)來說摇幻,又可劃分為展示部分(前臺)和內(nèi)部處理邏輯(后臺)兩大部分横侦;展示又可分為數(shù)據(jù)/頁面部分和接口部分。如此不斷的進(jìn)行細(xì)分和抽象绰姻,我們可以迭代出更細(xì)粒度的分類/層次枉侧,如下所示:

業(yè)務(wù):需要重點關(guān)注,我們的目的也是分離出具體的業(yè)務(wù)領(lǐng)域邏輯:

外部展示(表現(xiàn)層/接口層):數(shù)據(jù)狂芋、頁面(web)榨馁、遠(yuǎn)程接口(interface/api)?

內(nèi)部邏輯處理:應(yīng)用邏輯(應(yīng)用層/服務(wù)層)、具體業(yè)務(wù)邏輯(領(lǐng)域?qū)樱?

技術(shù):相對穩(wěn)定帜矾,具體業(yè)務(wù)無關(guān)(基礎(chǔ)設(shè)施層)

數(shù)據(jù)訪問(數(shù)據(jù)訪問層)?

日志翼虫、安全屑柔、異常、緩存等?

當(dāng)然珍剑,分類并不唯一掸宛,基于不同的視角我們可能會有不同的分類標(biāo)準(zhǔn)。比如數(shù)據(jù)訪問層也可以歸類到業(yè)務(wù)相關(guān)/內(nèi)部邏輯處理的部分招拙,因為可能涉及到一些對具體業(yè)務(wù)表的操作旁涤。

此外,根據(jù)問題領(lǐng)域和解決方案的復(fù)雜程度迫像,我們可以有不同的層次劈愚。比如在業(yè)務(wù)不太復(fù)雜時,我們可以把應(yīng)用層和領(lǐng)域?qū)雍喜橐粚印?/p>

DDD經(jīng)典分層架構(gòu)?

上面我們在分析分層的本質(zhì)時也提到了一些基本的層次和分類標(biāo)準(zhǔn)闻妓,但那只是一個非常粗粒度的劃分菌羽。

在實際決策時,我們需要知道各層的職責(zé)由缆、意義以及相應(yīng)的場景注祖;而落實到代碼層面時,我們還需要知道各層所包含的具體內(nèi)容均唉、各層的一些常見的具體策略/模式娃属、層次之間的交互/依賴關(guān)系辱挥。

首先我們來看一下Evans在《領(lǐng)域驅(qū)動設(shè)計》中提到的分層架構(gòu)沦补。

問:為什么要分成這樣的四層踢俄?

分層主要目的是為了簡化復(fù)雜性,系統(tǒng)中最復(fù)雜的部分應(yīng)該就是我們的業(yè)務(wù)邏輯层扶。當(dāng)系統(tǒng)交互或者工作流比較復(fù)雜時箫章,我們會考慮從業(yè)務(wù)邏輯中抽出這部分作為應(yīng)用層。而各個領(lǐng)域內(nèi)的代碼則化為領(lǐng)域?qū)泳祷幔@樣層級結(jié)構(gòu)更加清晰檬寂。

3.1 用戶界面層/表示層?

用戶界面層負(fù)責(zé)向用戶顯示信息和解釋用戶指令。這里指的用戶可以是另一個計算機系統(tǒng)戳表,不一定是使用用戶界面的人桶至。

該層包含與其他應(yīng)用系統(tǒng)(如web服務(wù)、RMI接口或web應(yīng)用程序以及批處理前端)交互的接口與通信設(shè)施匾旭。

它負(fù)責(zé)輸入?yún)?shù)的解釋镣屹、驗證以及轉(zhuǎn)換。另外季率,它也負(fù)責(zé)輸出參數(shù)的序列化野瘦,如通過HTTP協(xié)議向web瀏覽器或web服務(wù)客戶端傳輸HTML或XML描沟,或遠(yuǎn)程Java客戶端的DTO類和遠(yuǎn)程外觀接口的序列化飒泻。

可以看出鞭光,該層的主要職責(zé)是與外部用戶(包括web服務(wù)、其他系統(tǒng))交互泞遗,如接受用戶的反饋惰许,展示必要的數(shù)據(jù)信息。主要包含web部分和遠(yuǎn)程服務(wù)部分等史辙。

web部分一般包含常見的Servlet汹买,Controller等組件,而遠(yuǎn)程接口部分主要由Facade聊倔、DTO和Assembler等構(gòu)成晦毙。

Facade:遠(yuǎn)程外觀,一個粗粒度的外觀耙蔑,不含任何領(lǐng)域邏輯?

DTO:數(shù)據(jù)傳輸對象?

Assembler:對象組裝器见妒,負(fù)責(zé)數(shù)據(jù)傳輸對象與領(lǐng)域?qū)ο笙嗷マD(zhuǎn)換,不對外暴露?

問:參數(shù)的校驗為什么在用戶界面層甸陌?領(lǐng)域?qū)拥男r灪陀脩艚缑鎸拥男r炗惺裁床煌?

校驗應(yīng)該取決于校驗的內(nèi)容须揣,一般推薦盡早校驗,不過這里主要是進(jìn)行一些簡單的钱豁、不涉及業(yè)務(wù)規(guī)則的校驗耻卡。具體的業(yè)務(wù)規(guī)則的校驗放在領(lǐng)域?qū)印?/p>

問:為什么需要DTO?DTO和VO是同一個東西嗎牲尺?

領(lǐng)域?qū)ο箨P(guān)系比較復(fù)雜卵酪,很難序列化,而且用戶很多時候并不需要整個模型谤碳,大部分時候需要的只是其中的一部分內(nèi)容凛澎,DTO可以有效減少網(wǎng)絡(luò)調(diào)用的開銷。此外估蹄,領(lǐng)域模型內(nèi)部的邏輯也無需暴露給外部塑煎。

DTO一般用于遠(yuǎn)程服務(wù),如果是內(nèi)部使用的話臭蚁,一般可以直接使用領(lǐng)域?qū)ο蟆?/p>

VO中有前端狀態(tài)信息最铁,比如成功失敗等。

問:為什么需要Assembler垮兑?

主要目的是解耦冷尉,負(fù)責(zé)數(shù)據(jù)傳輸對象和領(lǐng)域?qū)ο笾g的相互轉(zhuǎn)換。BeanUtils也可以做到相應(yīng)的功能(dozer相對好一些)系枪,不過Assembler更為清晰雀哨,安全與可控,缺點在于手工代碼量稍多。

3.2 應(yīng)用層?

應(yīng)用層定義了軟件要完成的任務(wù)雾棺,并且指揮表達(dá)領(lǐng)域概念的對象來解決問題膊夹。該層所負(fù)責(zé)的工作對業(yè)務(wù)來說意義重大,也是與其他系統(tǒng)的應(yīng)用層進(jìn)行交互的必要通道捌浩。

應(yīng)用層要盡量簡單放刨。它不包含任務(wù)業(yè)務(wù)規(guī)則或知識,只是為了下一層的領(lǐng)域?qū)ο髤f(xié)助任務(wù)尸饺、委托工作进统。它沒有反映業(yè)務(wù)情況的狀態(tài),但它可以具有反映用戶或程序的某個任務(wù)的進(jìn)展?fàn)顟B(tài)浪听。

應(yīng)用層主要負(fù)責(zé)組織整個應(yīng)用的流程螟碎,是面向用例設(shè)計的。該層非常適合處理事務(wù)迹栓,日志和安全等抚芦。相對于領(lǐng)域?qū)樱瑧?yīng)用層應(yīng)該是很薄的一層迈螟。它只是協(xié)調(diào)領(lǐng)域?qū)訉ο髨?zhí)行實際的工作叉抡。

綜上所述,應(yīng)用層是表達(dá)user case和user story的主要手段答毫,主要用于協(xié)調(diào)領(lǐng)域模型與其他應(yīng)用組件的工作(并不處理業(yè)務(wù)邏輯)褥民。

應(yīng)用層中主要組件是Service,因為主要職責(zé)是協(xié)調(diào)各組件工作洗搂,所以通常會與多個組件交互消返,如其他Service,領(lǐng)域?qū)ο笤拍矗琑epostitory等撵颊。

一種比較常見的做法是:應(yīng)用層通常接受來自用戶界面層的參數(shù),再通過Repostitory獲取到聚合示例惫叛,然后執(zhí)行相應(yīng)的命令操作(很薄的一層)倡勇。

問:為什么要有應(yīng)用層?

業(yè)務(wù)比較復(fù)雜時嘉涌,我們會從業(yè)務(wù)邏輯中拆分出應(yīng)用層和領(lǐng)域?qū)印?/b>

如果在領(lǐng)域?qū)ο笾惺孪柔槍唧w應(yīng)用的邏輯妻熊,會降低應(yīng)用之間的可重用性。

此外仑最,如果將來需要加工作流之類的工具來實現(xiàn)應(yīng)用邏輯扔役,如果之前是混雜在一起的話則不好拆分。

3.3 領(lǐng)域?qū)?模型層?

領(lǐng)域?qū)又饕?fù)責(zé)表達(dá)業(yè)務(wù)概念警医,業(yè)務(wù)狀態(tài)信息和業(yè)務(wù)規(guī)則亿胸。

Domain層是整個系統(tǒng)的核心層坯钦,幾乎全部的業(yè)務(wù)邏輯會在該層實現(xiàn)。

領(lǐng)域模型層主要包含以下的內(nèi)容:

實體(Entities):具有唯一標(biāo)識的對象?

值對象(Value Objects): 無需唯一標(biāo)識?

領(lǐng)域服務(wù)(Domain Services): 一些行為無法歸類到實體對象或值對象上侈玄,本質(zhì)是一些操作婉刀,而非事物?

聚合/聚合根(Aggregates & Aggregate Roots): 聚合是指一組具有內(nèi)聚關(guān)系的相關(guān)對象的集合,每個聚合都有一個root和boundary?

工廠(Factories): 創(chuàng)建復(fù)雜對象拗馒,隱藏創(chuàng)建細(xì)節(jié)?

倉儲(Repository): 提供查找和持久化對象的方法?

關(guān)于各個元素的具體含義路星、職責(zé)以及相關(guān)誤區(qū)溯街,可參考領(lǐng)域建模核心概念解析.?

對于這些具體的對象诱桂,可定義一些標(biāo)準(zhǔn)領(lǐng)的Annotation來規(guī)范。

3.4 基礎(chǔ)設(shè)施層?

基礎(chǔ)設(shè)施層為上面各層提供通用的技術(shù)能力:為應(yīng)用層傳遞消息呈昔,為領(lǐng)域?qū)犹峁┏志没瘷C制挥等,為用戶界面層繪制屏幕組件。

基礎(chǔ)設(shè)施層以不同的方式支持所有三個層堤尾,促進(jìn)層之間的通信肝劲。?

基礎(chǔ)設(shè)施包括獨立于我們的應(yīng)用程序存在的一切:外部庫,數(shù)據(jù)庫引擎郭宝,應(yīng)用程序服務(wù)器辞槐,消息后端等。

作為基礎(chǔ)設(shè)施層粘室,Infrastructure為Interfaces榄檬、Application和Domain三層提供支撐。所有與具體平臺衔统、框架相關(guān)的實現(xiàn)會在Infrastructure中提供鹿榜,避免三層特別是Domain層摻雜進(jìn)這些實現(xiàn),從而“污染”領(lǐng)域模型锦爵。Infrastructure中最常見的一類設(shè)施是對象持久化的具體實現(xiàn)舱殿。

問: Repository作用是什么?和DAO的關(guān)系?

之前對Repository也曾有過誤解(在我們的系統(tǒng)中有一個repository層位于dao和service之間)险掀。?

DAO主要是從數(shù)據(jù)庫表的角度來看待問題的沪袭,并且提供CRUD操作(只是對數(shù)據(jù)庫表的一個封裝),是一種面向數(shù)據(jù)處理的風(fēng)格(事務(wù)腳本)樟氢;?

而Repository(資源庫)和Data Mapper(數(shù)據(jù)映射器)更加面向?qū)ο笾α担ǔS糜陬I(lǐng)域模型中。?

因為數(shù)據(jù)訪問層的暴露可能會破壞對象的封裝性嗡害,對象的關(guān)系和數(shù)據(jù)一致性也難以維護(hù)焚碌,所以 應(yīng)該盡量避免在領(lǐng)域模型中使用DAO模式,推薦使用聚合本身來管理業(yè)務(wù)邏輯霸妹。

模型的形態(tài)?

不同的架構(gòu)十电、不同的層、不同的應(yīng)用場景中有著不一樣的建模需求,因此表達(dá)相同概念的模型可能會有不同的形態(tài)鹃骂,例如:

充血模型:領(lǐng)域模型架構(gòu)中包含了領(lǐng)域邏輯和領(lǐng)域?qū)傩缘念I(lǐng)域模型台盯。?

失血模型:傳統(tǒng)三層架構(gòu)中只有g(shù)et/set方法,沒有業(yè)務(wù)邏輯的POJO對象畏线。?

貧血模型:類似充血模型静盅,但是不包括持久化相關(guān)邏輯。?

PO(Persistant Object):持久化對象寝殴,即DAO從JDBC取出來的對象蒿叠。傳統(tǒng)三層架構(gòu)中,PO即POJO組件中的對象蚣常,存在于DAO和Service之間市咽。?

DO(Domain Object):領(lǐng)域?qū)ο螅I(lǐng)域模型架構(gòu)中抵蚊,PO從數(shù)據(jù)庫取出來后施绎,有一個“重建”的概念,即根據(jù)數(shù)據(jù)還原實體贞绳,這個被還原的實體就是DO谷醉,存在于DAO和Service之間。?

DTO(Data Transfer Object):數(shù)據(jù)傳輸對象冈闭。對傳統(tǒng)三層架構(gòu)來說俱尼,該對象存在于Service和Controller之間。PO到DTO的轉(zhuǎn)換可以在Service或Controller中實現(xiàn)拒秘。?

VO(View Object):視圖對象号显。Controller在返回DTO給視圖時,可能還需要包括狀態(tài)信息例如操作成功/失敗的狀態(tài)碼躺酒、提示文本等押蚤。這時就需要在DTO外面再包一層,即View Object羹应。該對象存在于Controller和Web之間揽碘,由Controller進(jìn)行裝配

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市园匹,隨后出現(xiàn)的幾起案子雳刺,更是在濱河造成了極大的恐慌,老刑警劉巖裸违,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掖桦,死亡現(xiàn)場離奇詭異,居然都是意外死亡供汛,警方通過查閱死者的電腦和手機枪汪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門涌穆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人雀久,你說我怎么就攤上這事宿稀。” “怎么了赖捌?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵祝沸,是天一觀的道長。 經(jīng)常有香客問我越庇,道長罩锐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任悦荒,我火速辦了婚禮唯欣,結(jié)果婚禮上嘹吨,老公的妹妹穿的比我還像新娘搬味。我一直安慰自己,他們只是感情好蟀拷,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布碰纬。 她就那樣靜靜地躺著,像睡著了一般问芬。 火紅的嫁衣襯著肌膚如雪悦析。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天此衅,我揣著相機與錄音强戴,去河邊找鬼。 笑死挡鞍,一個胖子當(dāng)著我的面吹牛骑歹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播墨微,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼道媚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了翘县?” 一聲冷哼從身側(cè)響起最域,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锈麸,沒想到半個月后镀脂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡忘伞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年薄翅,在試婚紗的時候發(fā)現(xiàn)自己被綠了钞馁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡匿刮,死狀恐怖僧凰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情熟丸,我是刑警寧澤训措,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站光羞,受9級特大地震影響绩鸣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纱兑,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一呀闻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧潜慎,春花似錦捡多、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至倒信,卻和暖如春科贬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鳖悠。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工榜掌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乘综。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓憎账,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瘾带。 傳聞我的和親對象是個殘疾皇子鼠哥,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容