應用程序框架實戰七:分層架構的選擇


  建立應用程序框架,首先要考慮的問題是,你准備采用哪種分層架構,然后根據應用程序框架的邏輯層次來確定需要創建的VS解決方案和程序集。

  如果項目很小,需求很簡單,時間異常緊迫,且你手上沒有任何積累,那么,單層架構將是首選,最簡單的單層架構如下圖所示(為了集中你的注意力,我把不相關的文件都刪除了)。

 

  單層架構的主要優勢是代碼火力集中,干活直截了當,不像多層架構那樣拐彎抹角,每個操作都需要層層傳遞。對於上圖的User.aspx,所有相關代碼都直接寫到aspx頁面或后置代碼中,包括界面上的控件操作,業務操作和數據庫操作。

  另外,單層架構比較符合初學者的習慣,初學者對編程語法和.Net 基本API尚且不熟,哪會過多考慮代碼結構上的問題。

  使用單層架構,主要依賴於.Net強大的控件體系,拖控件,配屬性,寫事件處理函數,是單層架構下編程的真實寫照,這也是.Net程序員在外人眼里一直保持的印象。

  對於主要包含CRUD操作的小項目,單層架構可以工作得很好,但如果項目有一些比較復雜的業務邏輯,比如訂單流程 ,會發現代碼很快會變成一團爛泥,失去方向。業務邏輯與表現層代碼混雜在一起,其結果是界面上的任何修改都可能導致業務邏輯失敗。

  為了提升代碼復用能力,可以把每個頁面都需要處理的操作,比如權限檢查,日志跟蹤,全局錯誤處理等,提取到一個PageBase的基類中,放到Base文件夾,另外可以創建一個Helpers文件夾來放置公共操作類。

  單層架構為下一個類似項目所作出的積累微乎其微,業務上大部分代碼會被拋棄,有可能是技術的變化,比如之前使用Web Form,現在需要使用Win Form, 由於業務代碼和表現層代碼高度耦合,很明顯無法再使用之前的代碼。哪怕技術沒有變化,要在之前開發的爛泥中新增和修改功能,將是舉步維艱。有些邏輯上看似非常簡單的需求,當你實際動手時,發現並不是這么簡單,修改一處會牽動更多地方,所謂牽一發而動全身。

  對於技術上的積累,主要通過復制文件的方式供下一個項目使用。一個更好的辦法是把技術積累單獨提取到一個.Net類庫中,如下圖所示。

 

  這樣就實現了業務與技術的分離,當下一個項目到來時,只需要把Util.dll引入即可。

  為了挖掘單層架構的極限能力,需要開發一套強大的自定義控件,把數據庫操作、驗證操作、權限操作等內容全部封裝進去,在控件上設置數據庫列名,不需要經過DataTable或實體對象,直接生成Sql發送到數據庫。如果配上代碼生成器,開發簡單項目的速度驚人。

  對於單層架構,不論你怎么想盡辦法,它對復雜項目都無能為力,使用單層架構來開發業務邏輯復雜的項目,最終得到的就是一團亂麻。提升代碼質量可以在一定程度上緩解這些問題,但要從根本上解決,還必須向多層架構靠攏。

  從上面分析可以看到,單層架構不能勝任復雜業務領域,而且復用能力十分有限。由於應用程序框架的目標在於提升技術和業務兩方面的代碼復用,所以單層架構不是一個合適的選擇。

  信息系統從根本上來說,是對數據庫的操作,並且需要提供一套操作界面。如果按相關職責划分到不同的層中,會有助於項目的管理和代碼清晰度的提升。分層架構是SRP(單一職責原則)的一個應用,把高度相關的東西聚集在一起,把各個層用接口連接起來,這樣就能得到一個高內聚、低耦合的設計。

  目前.Net程序員普遍使用的分層架構是傳統三層架構,架構示意圖如下。

 

  傳統三層架構的核心在業務邏輯層,表現層會把數據放到實體中,作為參數傳遞給業務邏輯層,業務邏輯層會使用過程式的代碼來處理業務,如果需要訪問數據庫,則調用數據訪問層。

  傳統三層架構把表現、業務和數據訪問分離到不同的層中,當切換表現層技術,或更換數據庫時,只需要替換相關的組件即可,不需全部重寫。

  天下沒有白吃的午餐,在獲得清晰度、重用性,擴展性等好處的同時,也帶來了一些問題,主要是直觀性降低,復雜度提升,工作量增大。在單層架構下實現一個功能,可能只需要一個方法就能完全搞定,理解起來也比較容易,進入三層架構以后,每個操作都被分離打散到不同的層里,每個層有一部分代碼,需要在幾個層里來回跳動,才能窺其全貌,所以直觀性有所降低。另外,任何一個操作,需要在多個地方編寫,工作量倍增。

  如果嚴格按照三層架構的要求來編寫代碼,表現層盡量不要有業務操作,更不能有數據操作,業務層只管邏輯,這樣可以獲得比較高的可維護性。但是很多初學者對架構職責沒有認識,他們會隨便找個地方放置代碼,這個地方多半是表現層,這樣一來,不僅代碼質量很差,架構還很復雜,就得不償失了。

  傳統三層架構特別具有爭議的地方是Model實體層。這些實體從表面上看,好像是業務對象,比如客戶Customer,仔細觀察這個類,發現里面全是屬性,沒有方法,它用來充當數據容器,在各層之間傳遞數據。

  有些人一直認為自己是在進行面向對象開發,畢竟c#是個面向對象的語言,又使用了三層架構,還創建了實體,不就是面向對象開發嗎?實際上傳統三層架構還是面向過程的,只是披上了面向對象的外衣。還有些人對是否面向對象開發不屑一顧,“管它這些理論干啥,咱只搞實際的”,沒錯,一般程序員確實可以不管這些道理,但如果要追求更高的封裝性、復用性,可維護性,就必須向面向對象深入。

  面向對象的核心是根據概念建模,比如客戶,雖然傳統三層架構在Model層中確實創建了一個Customer類,但是Model層在三層架構中屬於輔助地位,是一個可有可無的東西,如果把Customer類換成DataTable也一樣可行。

  Model中的實體之所以用處不大,是因為違反了對象的特征,真正的對象是數據與操作的集合。有人把單純的數據對象稱為貧血模型,把具有數據和豐富操作的對象稱為充血模型,意指只有屬性的數據對象先天不足,營養不良,智商低,只會吃飯(數據),不會干活。

  為何對象一定要把數據和操作放到一起,才能發揮威力?因為這些數據與數據上的操作總是息息相關,且同時變化。把數據和操作封裝到一起,可以為操作提供唯一訪問點,主要好處是對操作集中管理,消除代碼冗余。當數據發生變化時,相關操作多半需要同時修改,由於代碼沒有冗余副本,且在對象內部完成修改,對外界甚至不產生影響,可維護性大大提升。

  把業務對象的數據和操作分離以后,第一個影響是業務邏輯散亂,難以管理。由於操作沒有一個統一的位置,所以沒有人知道這些操作究竟位於何處?可能在業務層,也可能在表現層,還有可能在存儲過程里。哪怕嚴格按照三層架構職責編寫代碼,所有業務邏輯都在業務層中,但可能很多個類都使用了這個實體,究竟某個操作在哪個類里?特別是在團隊作戰時,這個問題更加明顯,如果他不能很容易找到他要的方法,那么他就會自己添加一個,從而導致冗余。第二個影響就是導致明顯的代碼冗余,而代碼冗余是可維護性的天敵。每當數據發生變化或發現Bug,需要找到所有操作代碼的副本進行修改,如果有遺漏就會埋下地雷。

  當然,CRUD簡單操作不在以上討論范圍,上面主要指業務邏輯受數據的影響程度。

  可以看到,三層架構相比單層架構來說,已經有相當進步,但它還有一些缺陷可以改進,如果把Model實體層和BLL業務邏輯層合並,就可以得到面向對象模型,在《企業應用架構模式》中稱為領域模型。不過Eric Evans在《領域驅動設計》中以更具體的方式指導如何使用面向對象進行開發。

  應該采用充血模型(面向對象)、還是貧血模型(面向過程)?要不要使用ORM,要不要使用DDD(領域驅動設計)?這些問題總是爭論不休。不過我的建議是,如果你還准備在.Net界多混幾年,不如提早進行,因為這是大勢所趨,就好像單層架構最終被三層架構所取代。另外,領域驅動設計如果用得不好,充其量也就和三層架構差不多,所以你不需要有任何顧忌。

  本系列文章將采用Entity Framework和DDD分層架構演示應用程序框架的建設,所以本文暫不對DDD進行介紹。

  本文對分層架構的演化過程進行了簡單介紹,為你選擇適合自己的架構提供了一些參考,最后的結論是,無論你是否願意,由於軟件行業的發展,特別是微軟技術的推動,你始終會走上面向對象之路,與其被動挨打,不如主動學習。

  有些朋友發現這個實戰系列名不符實,全是廢話,一句代碼都沒有,還有些朋友喜歡四處搜集源碼,坐等干貨。我想說的是,這個實戰系列畢竟是介紹架構和框架的,如果你只拿到幾行代碼,沒有真正搞懂如何為你的項目建立應用程序框架,代碼無法形成一個體系結構,還是一團亂麻,那又有什么用。另外,我所提供的代碼也是四處搜集整理,沒有什么特別,你如果需要源碼,直接百度就會多如牛毛,但我希望你能搞懂每個細節, 這樣更有幫助。

  .Net應用程序框架交流QQ群: 386092459,歡迎有興趣的朋友加入討論


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM