|
今年做了兩個基于Rich Domain Model的系統(tǒng), 如何在UI層使用業(yè)務(wù)邏輯,公司之前的系統(tǒng)在這上面的處理上讓人非常不爽,自己重新設(shè)計(jì)了一套還是覺得有點(diǎn)別扭,拿出來給感興趣的人探討下。
先給出系統(tǒng)邏輯架構(gòu)簡圖

邏輯結(jié)構(gòu):
UI層使用WinForm,和后臺間傳遞DTO(DTO后面還會詳細(xì)介紹);
Business層邏輯上包含F(xiàn)a?ade層和Domain層,F(xiàn)a?ade不是簡單的對應(yīng)于Fa?ade模式,還兼容了Biz Flow Control, DTOAssemble(將Domain對象轉(zhuǎn)換成DTO對象或者相反)等Application Service方面的內(nèi)容和邏輯,Business層只有Fa?ade對UI層可見;
Persistence層負(fù)責(zé)和數(shù)據(jù)庫打交道,因?yàn)椴皇侵攸c(diǎn)就不多作介紹了。
我們規(guī)定UI層只能使用無行為的DTO對象,通過分析,我們發(fā)現(xiàn)大部分的界面都可以使用和Domain對象內(nèi)容一致的DTO對象,比如Domain里面有Order->OrderItem這么個Domain對象的聚合體,在UI的某個界面往往也正好只需要同樣的數(shù)據(jù)聚合體。所以我們劃分出EntityDTO和FlatDTO的概念,EntityDTO和Domain對象一一對應(yīng),只包含Domain對象的數(shù)據(jù),DTOAssembler有工具可以自動完成這兩者的映射,F(xiàn)latDTO就是任意數(shù)據(jù)內(nèi)容的組合(和PEAA里面的DTO的介紹一致)。
物理結(jié)構(gòu):
系統(tǒng)UI和Business層物理分離的,通過Remoting通信,公司之前的這種Remoting結(jié)構(gòu)的系統(tǒng)一直存在兩個問題,1)因?yàn)閃inForm提供了豐富的界面操作,有的人為了使用后臺和業(yè)務(wù)相關(guān)的邏輯頻繁的調(diào)用Remoting對象,完全不考慮效率,大部分操作背后都進(jìn)行了若干次的Remoting調(diào)用,2)有的人考慮到了效率,把很多不訪問到后臺資源的檢查和邏輯都寫到UI層,這樣導(dǎo)致邏輯分散難于管理難于重用。(我承認(rèn)UI必不可少得包含一些邏輯,比如非空輸入檢查,這種檢查往往UI和Domain都需要做的,但有些業(yè)務(wù)特別是計(jì)算還是只由Domain去處理好些)
為了緩解上述兩個問題,我考慮了另外一種稍微有點(diǎn)怪異的結(jié)構(gòu)。因?yàn)閁I層對RemoteFa?ade依賴于實(shí)現(xiàn),而非倚賴于接口,所以我們的后臺組件必須全部部署到客戶端(ok,我知道這樣不好,或許我會寫篇文章來介紹這其中的權(quán)衡),也就是說,如果不使用到后臺資源,我們完全可以在客戶端直接使用Domain,所以我們設(shè)計(jì)了LocalFacade和RemoteFacade兩個組件.RemotingFacade都是Remoting對象,UI層如果訪問RemoteFacade對象的話,肯定是要訪問后臺數(shù)據(jù)庫,后臺文件之類的資源;而LocalFacade是普通對象,如果UI訪問它,則LocalFacade首先將DTO轉(zhuǎn)化成DomainObject,然后使用Domain邏輯,而這一切都在客戶端處理,速度肯定比Remoting快得多(稍后才看到EJB的Local和Remote接口,不知道它的兩種接口是否可以同時使用)。
這樣處理最明顯的好處就是,從邏輯結(jié)構(gòu)上來講,所有和業(yè)務(wù)對象本身相關(guān)的檢查、計(jì)算等所謂的業(yè)務(wù)邏輯都可以全部駐留在Domain里面,而不會分散到UI層。另外一個好處是,如果客戶端已經(jīng)拿到了檢查或計(jì)算所必要的相關(guān)數(shù)據(jù),就不用費(fèi)力的往服務(wù)端再跑一趟了。
看到這里,細(xì)心的人可能有個疑問,為什么要使用EntityDTO,而不可以把Domain對象直接暴露給UI,這樣就可以省掉EntityDTO的維護(hù),EntityDTO和Domain映射的維護(hù)以及映射導(dǎo)致的效率損失(里面有很多反射操作)。Ok,這確實(shí)是我也看著不爽或許以后會改過的地方,之前的一些想法包括,1)UI層不應(yīng)該直接使用Domain的細(xì)粒度接口(實(shí)際上因?yàn)閃inForm提供的豐富的界面操作,以及大量的業(yè)務(wù)檢查,細(xì)粒度接口的訪問的概率倒是挺大的),2)Domain對象反轉(zhuǎn)倚賴于Persistence層的接口,因?yàn)槲覀冋J(rèn)為Domain在進(jìn)行某些檢查或者計(jì)算時,它所需要的數(shù)據(jù)可能還在數(shù)據(jù)庫里面,所以需要domain自身直接去訪問Persistence層,這種訪問是只讀的,也就是說,寫數(shù)據(jù)還是必須由Fa?ade層調(diào)用Persistence接口去實(shí)現(xiàn)。這樣做實(shí)際上并非必要的,也可以通過Fa?ade層獲取到所有Domain邏輯所需要的數(shù)據(jù),這樣Domain就可以與persistence完全隔離,不過這樣的domain邏輯給人感覺不連貫,相關(guān)的討論很多這里就不多說了。因?yàn)镈omain里面的行為可能回訪問到數(shù)據(jù)庫,所以,為了避免在UI誤用了這些接口,我們提供了LocalFacade,LocalFacade由后臺對Domain完全清楚的開發(fā)人員開發(fā),可以減少這種誤用的可能性,當(dāng)然這個理由很牽強(qiáng),畢竟通過測試就可以避免這個問題了。
在使用的過程中,還碰到另外一個頭疼的問題,比如判斷某個定單是否已經(jīng)發(fā)送,可以通過Order.Status =ORDER_STATUS_SENDOUT來判斷,但是UI人員建議提供Order.IsSend()的行為,這個要求如果在后臺的話,無可厚非,畢竟IsSend()的判斷邏輯很可能到后面不僅僅是對一個狀態(tài)的判斷,但是如果使用行為的話,就需要調(diào)用LocalFacade接口,也就是說OrderLocalFacade需要有IsOrderSent( OrderEntityDTOorder)的接口,然后在接口里面還需要把EntityDTO轉(zhuǎn)化成DomainObject(要么用N多的反射自動映射,效率降低;要么手動映射,維護(hù)成本巨大),然后才能通過Domain的行為來進(jìn)行IsSend()的檢查,LocalFacade暴露如此細(xì)粒度的接口,其維護(hù)的成本和發(fā)射的效率損失都是讓人很難接受的,要知道,客戶端這樣的需求實(shí)際上是非常多的。
總之,權(quán)衡是件很頭疼的事,尤其是使用分布式的架構(gòu),也難怪MartinFlower,RobJohnson等人都在極力呼吁不要使用分布式的架構(gòu)。上述的討論主要是針對C/S、Remoting的這種架構(gòu),在B/S架構(gòu)里面根本無須LocalFacade和RemotingFacade的區(qū)分,但是也會面臨Web層如何使用Domain邏輯的問題,再加上AJAX的引入,如何解決JavaScript里面直接包含業(yè)務(wù)邏輯的問題,應(yīng)該比我這里列出的問題更嚴(yán)峻。
最后,我考慮到另外兩種方案,以后或可嘗試,1)后臺使用RichDomain,前臺使用Manager。Manager都是靜態(tài)的方法,專門在客戶端處理業(yè)務(wù)邏輯。優(yōu)點(diǎn)是效率提高了,不用維護(hù)LocalFacade,不用把Domain部署到客戶端,缺點(diǎn)是后臺的業(yè)務(wù)邏輯會在前臺大量的重復(fù),無法使用到OO的繼承多態(tài)所帶來的靈活性。2)前臺不包含邏輯,所有需要訪問到業(yè)務(wù)邏輯的地方都走一趟服務(wù)器,這要求更好的設(shè)計(jì)利用DTO,盡量減少在一個操作背后往返服務(wù)器的次數(shù),優(yōu)點(diǎn)是開發(fā)人員爽了,缺點(diǎn)是客戶不爽了。好象又回到我最早提出的兩個問題上,唯一不同的是,之前的系統(tǒng)在這方面都沒有做特別的分析和約束,不同的開發(fā)人員在不同的地方都可能采用不同的方式,整個UI層使用Domain邏輯顯得很混亂,需要把這種無意識的狀態(tài)變成有意識的狀態(tài)。
-----------
看了age0的回復(fù),感覺誤解有點(diǎn)大,抱歉介紹得不清楚,補(bǔ)充一份概要的部署圖,注意客戶端和服務(wù)端的Domain是同一組件,在客戶端運(yùn)行的代碼,通過人為控制不會去訪問到服務(wù)端的資源(雖然domain里面有對存儲層的反轉(zhuǎn)依賴)
it知識庫:在UI層使用Domain邏輯的一些探討,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。