重析業務邏輯架構模式,淺淺滴
記得大約在一年前,我曾寫過相關議題的文章,叫做業務邏輯架構模式(事務腳本,表模塊,活動記錄,領域模型)和再談業務邏輯架構模式(事務腳本,表模塊,活動記錄,領域模型),經過這一年多做項目的沉淀,特別是最近十天來,我又仔細研讀了<Microsoft.Net企業級應用架構計設>這本書,并上了iteye論壇相閱了相關貼子,發現以前的很多認識是不全面的,甚至是錯誤的,相關的概念與認識也有了進一步的明確與清淅.覺得甚有必要將最新的一些想法與反思記錄下來,達到總結提高的目的.
一.總論
首先來看一張圖:
我的第一篇文章里曾出現過類似的圖,但是現在若干細節上有所不同,下面一一來分析
二.事務腳本
這是一種最直接的業務架構模式,他將后臺所有的模塊(包括我們常說的Dao,Server,Entity,BL層等)整合在了一起形成一個層,使用的是最徹底的用例驅動----UI上有什么樣的操作, 這一層就有什么方法.沒有專門表達數據結構的類,所有對數據的操作都是直接的Sql方式.
三.表模塊
雖然這種模式還是把所有的模塊都整合成一個層,但是已經開始有了對數據基本的抽象了.在.net里,就是將數據庫抽象成一個DataSet,將其中的每一張表抽象成一個DataTable.對數據的操作,就是對這些DataSet與DataTable的操作.一般來講,使用這種模式的BL層的類內置一個DataSet或DataTable類型的成員或屬性來裝載數據,所以這種BL類一般表達的是對整張表或者是對數據集合的操作,但是因為DataSet或DataTable在表達數據方面是弱類型的,所以在表模塊里是沒有辦法使用強類型的對象的.
四.活動記錄
雖然這種模式還是把所有的模塊都整合成一個層,但是與表模塊相比,有兩個值得注意的地方.
1.在這種模式里,對于數據的抽象就更進一步了----通過對象來表達數據,也就是對數據的強類型抽象.
2.一般來講,使用這種模式的BL層的類,其屬性就是數據庫的列,一個對象就是數據庫中的一行數據,所以其表達的操作是對數據行的操作.這也是與表模塊模式最大的不同.
在.net里,ActiveRecord框架就專門表達這種模式的框架,通過其底層封裝的類庫與代碼生成工具, 能夠很快生成與這種模式相適應的.net代碼.
五.領域模型
這種模型是我這篇文章的重點.在領域模型里,通過對BL層的分解來達到復用與靈活.按照iteye資深會員robbin的觀點(我也基本贊同),特將此模型按實現方式的不同再分為以下4小類:
1.失血模型
在這種模型里,有一個Entity類來抽象數據,其只有屬性,沒有方法來表達業務,還有一個EntityManage類來表達領域邏輯與業務邏輯.
2.貧血模型
在這種模型里,有一個Entity類來抽象數據,用屬性表達數據,還有少許不依賴其它層次與技術的領域邏輯,用屬性或方法表達.還有一個EntityManage類來表達依賴其它層次與技術的領域邏輯和業務邏輯.
3.充血模型
在這種模型里,有一個Entity類來抽象數據,用屬性表達數據,還有大部份的領域邏輯與業務邏輯,用屬性或方法表達.還有一個EntityManage類,僅僅封裝事務和少量邏輯,主要起一個外觀的作用.
4.脹血模型
這種模型與活動記錄比較像,就是不再分層,而是把所有的層全都放在一起.
在這四種模型當中,失血與脹血是應該不被提倡的.脹血模型與活動記錄差不多,而把其中所有的邏輯拿到另外一個類中后就成了失血模型,兩個都走了極端.剩下的兩個模型:貧血模型與充血模型,就要視情況而定了.在我看來,我更推崇貧血的領域.如下圖所示:
先來解釋兩個名詞:領域邏輯與業務邏輯.所謂領域邏輯,就是領域本身特有的,不依賴具體執行環境的邏輯.比如一份合同,有簽訂時間與有效期,那么計算過期時間就是他的領域邏輯.業務邏輯,則是與領域相關,依賴特定執行環境的邏輯.比如合同訂金,是依賴于合同的種類與金額大小的,有的15%,有的3%,有的簽訂合同當場就要繳納,有的則有其它的條件與時限.
首先看領域層.領域層分為兩部份:領域對象與領域邏輯.領域對象就是領域層的靜態部份,通常表現為屬性,在大部份的程序里他的大部份都會與數據庫的列一一對應.領域邏輯是領域層的動態部份,是那些不依賴其它層次與技術的領域邏輯.在上圖中可以看到我把領域層與表現層銜接了起來,表示領域層的對象也可作為DTO傳到表現層上面去.
然后再看服務層.包括了依賴其它層次與技術的領域邏輯和業務邏輯.服務層是系統的核心,系統中的各個層次與各種技術都要與他發生交互.在上圖中,上承表現層,下調領域層,通過IOC注入執行環境,通過AOP分離系統邏輯,通過ORM透明持久化,通過MSDTC定義事務邊界等等.
在這里,有幾點需要重點說明
A 在我看來,領域層是可以復用的,是面向對象的,為了達到這個目的,就一定要保證領域層的"純潔" .所謂"純潔",就是指:a) 領域層不能依賴任務具體的執行環境.比如我現在在做國土業務,政府在賣地的時候,會發幾樣憑證,其中一個叫<建設用地批準書>,一個叫<供地合同>.全國各地都是這么發的,但是發證的順序與科室卻有所不同,有的是M部門發A,N部門發B,有的是Q部門AB一起發.為了使這兩個領域模型重用,就必需不能包含以上發證邏輯;b) 領域層不能依賴于具體技術實現.比如現在用的比較多的是NH,但是我建議還是不要讓領域層依賴于他.萬一重用后的環境用的是EF,就麻煩了.有時,為了簡化程序層次,也可作為DTO傳到表現層上面去.
B 在我看來,服務層是用例驅動的,是面向過程的,大部份情況下是無法實現復用的.為了幫助領域層保持"純潔",服務層就不得不"不純潔"了.就如同上面所說的,上承表現層,下調領域層,通過IOC注入執行環境,通過AOP分離系統邏輯,通過ORM透明持久化,通過MSDTC定義事務邊界等等.系統中所有的一切,都在服務層形成了交匯.
C 在Asp.Net WebForm環境中,有時簡單起見,會把頁面的CodeBehined代碼與Server層合并.這樣可以減少系統的層次.不過如果系統比較大,比較復雜,還是不要這么干.
D 真正好的領域模型是重構出來的.一個行業的信息化是一個從不穩定到穩定的過程,一個業務的理解也是由淺入深的過程.在一開始,可能我們不確定這是不是個核心業務,業務的具體表現形式也可能多變,這時可能我們會把邏輯寫到CodeBehined里;經過一段時間的沉淀后,可能覺得這個業務較為通用,表現也趨于穩定,這時我們就可能會把其重構到Server層里;最后我們發現其實這個業務經過更高級別的抽象后有其不變的地方,這時我們就會考慮將其技術細節剝離后放入領域模型.
E 在我看來,復雜查詢,報表與統計在本質上不是面向對象的,而是面向數據的,且業務非常不穩定,那么他們也就不必通過領域模型來完成.而是直接從Server層連接到DB來完成相關功能.
F 領域驅動設計與領域模型雖然經常配合起來使用,但其實是兩個層面上的東西.領域驅動設計,強調的是在業務分析過程中,以業務為核心進行分析建模,是一種分析方法論;而領域模型則是一種強調在技術實現中,以業務為核心,是一種技術實踐的方法論.
在我看來,充血模型會提高領域層的耦合性,在業務上耦合了特定業務場景,在技術上耦合了特定技術選型,所以我更傾向于使用貧血模型.
其實以上文章只是我在結合了自己的理解后炒的冷飯,早在5,6年前,iteye論壇就已經組織討論過相關話題,02年POEAA就出版了,想到自己的相關技術水平還只停留在別人10年前的水平,感覺很是慚愧.
以上的貧血模型,其實只能比較好的解決中小規模的項目.一旦項目復雜度上到一定的程度后,此模型仍然運作的不是很理想.下一步,就是要進一步的研究近些年出現的相關知識,如DCI,四色模型,CQRS等.爭取從更高層次完成對系統的拆分,實現系統更理想的復用!在這方面,園子里已經有兩位先行者了:dax.net與netfocus.有時候,能站在先驅與巨人的肩膀上學習,真是件很幸福的事啊~~~
引用的文章
一.關于領域模型
二.貧血VS充血
為什么java里不能把域對象和DAO合并,rails里面就可以?
對Robbin《domain model的延伸討論(重新編輯) 》一文質疑
Domain Object貧血vs富血(DDD)和spring roo到ruby的扯淡
三.其它

浙公網安備 33010602011771號