天天看點

對企業級應用開發的思考--分層

    首先聲明,本文并不是介紹什麼是N層架構,然後給張分層圖,最後來一堆代碼結束。本文主要是對分層過程中常常讓人感到困惑地方的思考,以及最近園子裡面讨論異常激烈的一些問題的再讨論。本文從個人經驗角度出發,努力嘗試來解決這些困惑,歡迎拍磚,但,如果你進行人生攻擊,我也隻能在心裡畫個圈圈詛咒你一下!

開始

我們先從一幅大家眼熟能詳的圖開始:

對企業級應用開發的思考--分層

這是應用開發人員最熟悉的N層架構圖,其中:

  • 資料通路層:應用程式中全權負責與資料存儲對話并持久儲存和檢索業務對象的層。通常,資料通路層包括所有的CRUD 方法與查詢機制,使得業務邏輯層能夠針對任何給定的條件檢索對象。
  • 業務邏輯層:它包含定義和處理複雜業務功能的所有規則、工作流和驗證邏輯,設計軟體以滿足這些複雜的功能;困惑最多的地方就是這一層。
  • 應用層:封裝業務模型,并為所有相關的應用程式提供接口。關于應用層,有一個十分讓人困惑的地方,後面會詳細說明。

    這是三層的簡單定義,當開發者看到這些定義的時候,基本都會有這麼一個感覺:哦!也就這麼回事。可是在實際編寫代碼時,尤其是随着項目代碼量越來越多,業務越來越複雜的時候,會明顯覺得:

    要改這個業務,又要去改資料層的CRUD,太難受了;

    這個操作不分層,我一個函數調用就搞定了,為什麼一分層,我要嵌套這麼多層,太惡心了;

    ...

當你有這些感覺的時候,請停下手頭上的工作,思考一下,有沒有因為遇到下面這些情況讓你感覺到困惑。

困惑1:層之間的依賴關系

對企業級應用開發的思考--分層

    該圖引用自jesse liu的部落格(如有侵權請告知,我會在第一時間修改,實在是懶得畫),我覺得很形象。上面是常見的三層中的依賴關系。下面是在領域驅動設計下的依賴關系。不要小看這個圖,可以肯定的是90%以上的開發人員在開發過程中對N層架構的依賴關系是這樣的,哪怕是在使用領域驅動進行設計時,無形中也擺脫不了這種依賴關系。初學者一般覺得這種依賴關系很正常啊?本來就應該是這樣的。造成這種思想的根本原因是初學者通常覺得DAL層(或DDD中的repository)可以友善給其他系統調用呀?不就實作複用了嗎?我的BLL層也可以....

    打住,請打住!!你的BLL層能複用?你的BLL層已經在依賴DAL了。

    DAL能複用?是能複用。但,對于特定的開發系統來說,DAL層的複用毫無意義。你會把圖書管理系統中的DAL複用給部落格網站嗎?除了能複用最基本的ADO操作之外,你什麼都複用不了。

    是以,層之間的引用應該設計成這樣的:

對企業級應用開發的思考--分層

    好吧,圖還是jesse liu的。這個圖很形象的說明了我們該如何處理層之間的依賴關系。因為系統中真正可以複用的其實是這樣的BLL層。它不依賴任何層,對于特定的系統來說,無論你的資料庫變了,界面變了,但核心的業務其實是比較穩定的。其實該實作方式的核心思想就是大名鼎鼎的依賴倒置。實作方式可以使用反射或IOC等進行,可以參考園中其他小夥伴的文章或我的另一文:通用資料采集平台,從架構到代碼 。

困惑2:業務邏輯層實作方式的選擇

    業務邏輯層實作方式有三種:事務腳本、活動記錄及領域模型。現在園子裡面大興DDD之風,對領域驅動設計推崇倍至,你要是和他說其實這個用事務腳本封裝一下,那個來點活動記錄集搞搞就行了。保證他立馬噴的你體無完膚。常言道:存在即合理。前兩種實作模式自打程式設計出現以來,有着悠久的曆史。這裡我們簡單介紹一下它們及說明一下他們的适用場景。

事務腳本(Transaction Script)

    名字叫的很玄乎,其實說直白點它就是使用一系列功能函數來實作系統的業務邏輯。它遵循面向過程的開發方式,而不是面向對象的方法。核心思想是為每個業務建立一個過程,每個過程都包含完成業務事務所需要的所有業務邏輯,包括從工作流、業務規則和驗證檢查到資料庫持久化儲存的所有内容。

    從各大經典教材上來看,它适用于具有很少邏輯或沒有多少邏輯的簡單應用程式,在使用者界面中實作所有的業務邏輯。在實際操作中,将應用程式分成小的功能子產品,分别将它們實作成使用者界面,并在其中嵌入業務規則。這時采用自動化程度最高的使用者界面建立工具(比如ASP.NET中的伺服器控件)和可用的可視化程式設計工具進行開發。

活動記錄(Active Record)

    該模式對于資料庫中的每個表都存在一個對應的業務對象。業務對象代表資料表中的一行,在業務對象中包含資料和行為,同時包含用于持久化對象的方式及添加新執行個體和查找資料集合的方法。

    适用于業務隻是在資料庫之上加一個顯示處理界面。在有些精典書籍中提出,以EF與linqtosql為代表的資料通路對象模式(DAO)最适合的就是這種場景。它們都通過一個資料上下文(DbContext)作為入口,實作業務對象與資料表的對應。但個人認為EF(linqtosql用的較少)經過多個版本的演化,已經擺脫了一對一映射的限制。完全适用于領域模型。

領域模型(Domain)

    該實作模式與活動記錄非常相似,它與活動記錄集的主要差異就是:領域模型的業務實體不知道如何持久化自身,且資料模型與業務模型之間并不是一定要存在一對一映射的關系。

    關于該模型的文章有很多。這裡就不詳細介紹。它适用于對複雜業務邏輯進行模組化,至于這個複雜業務邏輯的标準是什麼,我也在探索中。我想有些開發者使用它是為了表現自己的技術能力。其實完全沒有必要。個人覺得了解它的思想就行,在一些小項目中能不用盡量不要去用。折騰自己也折騰同僚。

    這裡要特别說明一點的是:因為業務實體并不知道自己如何持久化,是以領域模式依賴于ORM或Repository模式來持久化。我們在這裡的依賴并不是在設計時領域要依賴下面的資料操作,而是領域層最終要靠ORM或Repository來實作資料存儲的讀寫。DDD設計中最大的争論也就此展開:Repository模式到底有沒有必要,尤其在使用EF的情況下。我們單獨為此開一個專題。

困惑3:Repository模式

Repository的争論曾經在園子深挖DDD的幾位牛人圈子裡面持續了一段時間:

初探領域驅動設計(2)Repository在DDD中的應用

Repository 倉儲,你的歸宿究竟在哪?(一)-倉儲的概念

尤其是當用EF實作Repository時,争論可以達到慘烈的程度: 

部落格園的大牛們,被你們害慘了,Entity Framework從來都不需要去寫Repository設計模式

那段時間我也是看着他們的文章,陪着他們一起困惑,一起糾結。但某個風高月黑的晚上。我突然間有了以下想法:

    領域模型的業務實體不知道如何持久化自身,如果我們想把這些實體存儲到電腦中,我們必定需要一個可以持久化的方法。方法太多了,ADO.NET、linqtosql、EF等等,于是我們提取一個資料化的接口在業務邏輯層,取個名字叫Ixx..叫什麼呢?Martin Fowler大大說叫IRepository吧,于是Repository模式就出來了。記住:這裡在業務邏輯層裡面添加的是接口,而不是實作,這很重要!

對企業級應用開發的思考--分層

    其實我贊同Leo C.W在那篇慷慨激昂的文章裡面提到的觀點:EntityFramework 本身就是基于Repository設計的,我也贊同他說的那些EF+Repository包裹後的缺點。但,這不足以讓我們抛棄Repository,哪怕是和EF做搭檔。當他們兩個一起工作的時候,Repository可以了解為設計模式中的擴充卡模式。為了保持整個設計的接口統一,為了協調領域和資料映射層。因為如果在業務邏輯層裡面直接使用EF,你的領域就已經不再是純潔的領域。是以我在上面一段結尾的時候強調了在業務邏輯層裡面添加的是IRepository接口。

    而且就像我在困惑2中提到的一樣,當你選擇使用領域模型的時候,就可以假定你所處理的業務邏輯比較複雜,而且需要兼顧擴充性(比如資料庫更換),是以Repository有存在的意義。如果你的項目屬于短期的項目,或者說你不用考慮更換資料通路層,你大可直接選擇活動記錄模式,那麼如果你不想用Repository,那就不用。不然直白點說:你用領域就是為了裝那個啥。

困惑4:應用層

    很多人覺得這一層很簡單,就是處于界面與業務邏輯之間的一個外觀模式。但,就像田園裡的蟋蟀說的一樣:

    有時候我們在領域驅動設計的時候最容易混淆的就是應用層和領域層,網上關于領域層和應用層的定義概念一搜一大把,你可能也會說幾句,比如什麼應用層是很薄的一層,主要工作是協調任務的等等,但是實踐起來呢?用代碼表示就蒙了。

    對于某個XXService,你會無比糾結它到底是放在業務邏輯層中還是應用層中,尤其是像我這種對代碼整齊有潔癖的人,更是痛不欲生。蟋蟀後來提出:

應用層很薄,所做的工作是:
  1. 發起一個請求
  2. 确認處理結果
  3. 送出工作單元

    看到他的這個觀點,瞬間讓我想到《ASP.NET設計模式》這本書中的一個觀點:Document Message消息傳送模式。作者在應用層裡面唯一做的一件事情就是把所有的界面邏輯封裝成一個Request,然後通過業務邏輯層中的功能組合計算出一個Response,傳回給界面(詳見該書6.3.2章節)。

對企業級應用開發的思考--分層

    綜合上面的兩點經驗,也算是給我們指明了一條怎樣使應用層“變薄”的出路了。

總結

    真正對分層進行研究是因為公司裡面一個水務集團的項目,資料量業務邏輯都相對比較複雜,當時硬着頭皮要求使用領域驅動設計,結果很悲慘。項目一半的時候大家都被折磨瘋了,那時才知道我們離真正的模組化差距有多大。我們以前的分層,無非是借着面向對象的外殼,使用類來進行面向過程的分層罷了。中間為了設計模式而設計模式,為了分層而分層。後來項目雖然完工,但遠沒達到我心目中的期望。于是靜下心來,看了園子裡面很多這方面的文章,記錄下些許心得。還請各位大大不吝指教,感謝!

作者:李玉寶(李玉寶的代碼人生)

出處:http://www.cnblogs.com/yubaolee/

開源架構:最好用的的權限工作流架構OpenAuth.Net

本作品采用知識共享署名-非商業性使用-禁止演繹 2.5 中國大陸許可協定進行許可。

如您有任何疑問或者授權方面的協商,請給我留言。