2013年7月12日 星期五

ASP.NET MVC 專案分層架構 - 建議與補充說明

2014-12-02 補充說明:
這一系列的文章並不適合初階及中階的開發人員,如果你是程式開發的初學者或是 ASP.NET MVC 初學者,甚至是開發經驗少於兩年的開發人員,請馬上離開此篇文章。

從去年 (2012) 的十月開始一直到今年 (2013) 四月之間,我一共寫了六篇有關專案分層架構的文章,從一開始的怎麼在單一的 MVC 網站專案去抽出資料存取的部分為 Repository,接著再把 Repository 從網站專案分出來,自成一個類別庫專案,再來就是把屬於商業邏輯的部份抽出來成為 Service,然後就是如何使用 IoC Container。

很多人對於這個系列有很大的興趣,但也有很多人對於這些文章的內容有很多的疑問與困惑,從文章下面的留言回覆就可以感受到,甚至於在 twMVC 的每週四固定聚會時都會當面詢問有關這系列文章的相關問題。

有鑑於目前對於有這麼多問題的情況下,在進入此系列的下一篇文章前,實在需要來個中場休息時間(這個中場休息時間還真久,距離上一篇的系列文章已經快要三個月了),藉由這一篇文章來做個補充說明與看這個系列文章的一些建議。

 


「分層架構」系列文章截止目前的文章如下:

ASP.NET MVC 專案分層架構 Part.1 初學者的起手式

ASP.NET MVC 專案分層架構 Part.2 抽出 Repository 裡相同的部份

ASP.NET MVC 專案分層架構 Part.3 - 個別 Repository 的資料存取操作

ASP.NET MVC 專案分層架構 Part.4 - 抽出 Model 層並建立為類別庫專案

ASP.NET MVC 專案分層架構 Part.5 - 建立 Service 層

ASP.NET MVC 專案分層架構 Part.6 - DI/IoC 使用 Unity.MVC

 

問題一、需要全部看完文章能做分層嗎?

不需要,我這一系列的文章是採漸進的方式,把一個最基本 ASP.NET MVC 網站(資料存取與商業邏輯都在 Controller 裡)去將網站的不同部分做修改,讓資料存取以及商業邏輯的部份分別抽離。

Part.1 可以了解到資料存取的部分從 Controller 裡抽離出來的做法。

Part.2 與 Part.3 則是再去進階說明 GenericRepository 與個別 Repository 的建立與不同。

Part.4 則是把網站專案裡的 Model 部分抽離出來並另外建立類別庫專案,讓資料存取的做不會出現在 Web 專案裡,Web 專案只專注於 Web 的呈現以及去呼叫使用適當的 Model 層方法來完成需求

Part.5 這一篇則是把 Model 層裡面有關商業邏輯操作的部份給抽離出來,讓 Model 層只專注於資料存取的操作上,因為往往有許多人會讓資料存取與商業邏輯的程式混在一起,會讓日後的維護、修改與重構都會相當困難,所以適時地把商業邏輯給抽離出來,可讓各個層別專注於各自的職責。

一般來說,在小型的專案裡使用上面幾篇文章裡面的操作就足夠了,但如果想要讓專案更為嚴謹,甚至於是開發大型專案的應用上,上面的文章內容將會無法應付,在專案裡加入 IoC 的應用,降低各層之間的耦合可讓專案的架構更為彈性,這就是 Part.6 這篇文章裡所提到的,如何在專案裡加入 IoC Container。

這邊建議各位採取漸進的方式,看到一個階段,把這個階段所看到的作法應用在實務或是練習上,讓自己藉由專案實際應用的過程去消化,然後再內化到你自己的觀念裡,當你接受了這樣的作法之後,再繼續閱讀下一個階段的內容。

 

問題二、一定要把 Repository 與 Service 都抽出來做類別庫專案嗎?

可以不用,在小型專案裡還大費周章地將 Repository 與 Service 給抽出來做類別庫專案,其實這還蠻費工的,所以像小型專案(例如活動網站、簡單的官網、訊息公告的網站等等),這些不需太過於複雜處理的網站就不必要去做 Repository 或 Service 的類別庫專案,只需要在單一的專案裡面就好,以目錄的方式來做區分,C# 的類別在建立時會依據檔案所在的目錄位置來產生 Namespace,所以只要目錄管理做好,就可以在單一專案裡做分層。

 

問題三、一定要用介面嗎?

其實沒有介面的實作也是可以 Work 的,想想我們一開始寫 ASP.NET 的時候,有多少人了解什麼是物件導向呢?(再一次聲明,不是用 C# 或 VB.NET 所寫出來的程式就是物件導向,要看你是否以物件處理的方式來處理資料、ˋ是否以物件的觀念來思考每個流程裡的資料傳遞處理等等),在不了解物件導向的情況下,我們還是可以解決各種問題與需求,為什麼要用介面?其實要看你是否接受這樣的做法,在程式實作之前,先規劃介面的內容,讓開發者了解要開發的範圍,而不是任由開發人員自由發揮、無限發散,另外除了可以方便實作單元測試之外就是可以增加系統的彈性,降低各層之間的依賴。

也許很多人所開發的專案都是在限定規格上來做開發的,例如專案規格裡就明定只有使用 Microsoft SQL Server 而不會改用其他的資料庫系統,像這種就不會遇到更換程式裡資料存取方式的問題,但假如你所開發的是一個產品,或是一個開發規格的專案,資料庫系統不一定是使用 MS SQL Server 的情況下,在沒有制定介面的專案裡,你要怎麼應付這種多資料庫抽換的狀況?難道是一個資料庫系統就寫一個對應的專案嗎?如果有 10 種資料庫系統的話,就要做出 10 個一樣功能但是資料庫存取不同的專案嗎?

專案要面對不同資料庫系統的情況是比較少,但還有另一種情況,系統內的資料存取方式,目前依然使用傳統 ADO.NET 來存取資料的系統還是有很大一部分,雖然 ADO.NET Entity Framework 以及其他 ORM Solution 已經發佈這麼多年,但還是有很多專案並沒有使用,其中主要的原因在於很多開發人員甚至那種半途出家當 SA, PM 的人並沒有物件導向的觀念,所以要他們在專案裡使用 EF 是相當困難的事情,因為觀念轉不過來,物件導向的做法讓他們覺得麻煩,還不如敲敲鍵盤然後產出 SQL Statement 去直接執行就可以得到他們所想要的資料;如果他們遇到專案的存取方式需要改變,不再繼續使用 ADO.NET 來存取資料,在沒有建立介面的情況下,通常都是直接在程式裡就包含了資料存取處理,當遇到要改的情況,這種你濃我濃的程式就無法做修改,因為已經濃的化不開了,像這一種情況,如果事前有建立介面而且也有把資料存取處理給抽出為 Repository,那麼資料存取從 ADO.NET 改成其他的方式,都不會影響原本的程式,因為原本程式所對應的資料處理是介面,只要是有實作介面的都可以接得上。

原則上,我會建議各位要建立介面,而且介面與實作要分開(時常看到那種介面與實作都擺在同一個檔案裡的作法,像這種作法,還不如不要用介面),讓我們建立專案時就保持彈性的做法,在專案逐漸龐大、複雜的時候,在維護、修改時就可以感受到好處。

 

問題四、寫 ASP.NET MVC 就一定要做分層嗎?

無論是 ASP.NET Web Forms 或是 ASP.NET MVC 所開發的網站專案,不做分層也是一樣可以開發並且正常運作,但是做分層的原因是讓責任區分以及維護管理,在 Web Forms 專案的開發上最常遇到的狀況就是 Code-Behind 裡面常常會在 Page_Load 或是 Button1_Click 的 Event 裡把頁面操作、資料存取、商業邏輯處理都混在一起,這樣的程式除了難以維護之外,另外就是責任難以區分,常常會搞不清楚是那一個環節出狀況讓頁面顯示出現錯誤,或是哪個設定的關係而讓商業邏輯的處理錯誤等等,在 MVC 裡面的話就是 Controller 的 Action 方法裡面塞了資料驗證、資料處理、商業邏輯處理等,跟 Web Forms 把所有處理都在 Code Behind 的方式還不是一樣,一樣難以維護、責任無法區分。

做分層,為的就是讓開發更加流暢、避免雜亂、維護容易;所以要不要做分層,就看你日後的維護是否願意面對雜亂、維護不易、責任不分的程式,反正不做分層,系統還是可以執行,但執行的時候並不是耗費你的時間,而開發與維護所耗費的是你的時間。

 

問題五、看這一系列文章是否要先了解 Entity Framework 呢?

是的,最好要先了解 Entity Framework 以及 LINQ 語法操作,因為在文章裡到處都看得到,尤其是前四篇文章都是在 Model 層上面打轉,所以對 Entity Framework 以及 LINQ 不了解的朋友就會完全看不懂。

而 ASP.NET MVC 的 Model 並不等同於 Entity Framework,這對於很多初學或是想了解 ASP.NET MVC 朋友最大的困惑,因為很多教學文章以及書籍裡面都是使用 Entity Framework 來做為 Model,以致於許多人就覺得 ASP.NET MVC 的 Model 就是 Entity Framework、如果不學 EF 就無法使用 ASP.NET MVC 的誤解,ASP.NET MVC 的 Model 也是可以使用傳統的 ADO.NET 方式,在之前我曾經寫了一系列的相關文章來說明在 ASP.NET MVC 的 Model 使用 ADO.NET,並且以簡單分層還是建立介面的方式,讓大家了解到事先建立介面,然後使用不同資料存取方式的作法,可以讓系統的 Model 隨時抽換,看是要用 ADO.NET 還是 EF,都不會影響到 Web 的程式處理。

ASP.NET MVC 的 Model 使用 ADO.NET

ASP.NET MVC 的 Model 使用 Enterprise Library 6 Data Access Application Block

ASP.NET MVC - 使用 Simple Injector 讓 Model 三選一

ASP.NET MVC 與 ASP.NET WebForm 使用 Simple Injector 切換選擇不同 Repository 原始碼下載

 

問題六、我要用 IoC/DI 嗎?

其實這個問題跟上面問是否一定要做分層的問題有些類似,我的回答是「可以不用,要不要用是看你有沒有這樣的需求以及專案為什麼要用」,系統開發時要用什麼樣的 Solution 來解決問題,通常除了這個 Solution 可以符合解決問題的需求之外,就是使用這個 Solution 是不是可以讓系統開發得到更好的幫助,所以用不用 IoC/DI,不是由我來跟你說,而是你要問自己為什麼要用。

 

問題七、IoC Container 可以不要用 Unity.MVC 或是 Enterprise Library Unity 嗎?

當然可以不用 Unity.MVC,可以改用其他的 IoC Container,例如很多人使用的 Autofac 以及 Castle Windsor 等等,當初會使用 Unity 來做為文章當中的 IoC Container,是因為 Unity 為 Enterprise Library 的其中一個 Block,使用以及操作都是我比較熟悉的,另外就是可以直接從 Nuget 上直接取得,而且有人提供 ASP.NET MVC 的整合,最重要的是使用上並不會太過於複雜。

IoC Container 是可以依據你的習慣、系統需求與是否適用來做考量,如果未曾使用過 IoC Container 的人,我會建議不要一開始就聽別人說哪個套件很強,可以支援很多功能,而是要先選一個比較容易入門的,然後之後的專案再依據需求以及自己的程度再改用其他的 IoC Container。

 


分層架構的相關資訊與做法,每個人以及每個專案所實作的方式都會不同,所以我這系列的文章也並非就是最佳實踐的方法,但可以是學習如何做分層架構的參考,藉由每篇文章不同步驟的操作,讓大家知道這些步驟的前因後果。

我喜歡到 CodePlex 或是 GitHub 去挖寶,只要是 ASP.NET MVC 相關的專案都會把原始碼給載下來看,從別人的專案架構裡去學習以及思考他們為何什麼這麼做,我是這樣學習並且歸納整理之後,才有我自己的一套分層架構的實作方法,或許存有很大的問題,但沒有任何的實作與寫法可以一次到位,程式設計都是需要經過不斷的學習、嘗試之後才能淬鍊而成,而我還在學習與嘗試的階段。

分層架構的做法,在我這些年的職涯過程中只有一家公司用非正式的方式來教我,為什麼說是「非正式」呢?因為並沒有正式的教學或是任何的教育訓練,因為那一家公司有自己所發展的 Framework,所有開發的專案都必須在這個架構裡去做開發,在進入家公司之前的我,在開發 ASP.NET 網站都是一個專案到底,然後最多只會把資料存取的相關程式操作給抽出來到 App_Code 裡,所以當我必須用著不熟悉的 Framework 做系統開發的時候,對我而言有相當大的衝擊,當然一開始的不習慣與困惑是相當多,但是在專案開發的過程以及其他同事討論裡逐漸了解開發的方式,然後藉由向架構師的詢問,才知道什麼是物件導向並且學習到怎麼做分層架構開發,這一段過程的詳細內容我會在另一個系列文章「我的程式學習之路」裡再做描述。

不管怎樣,文章看歸看,最重要的是你要動手下去做,唯有真正實作過後才能知道怎麼做分層架構,不然文章從頭到尾看到完,裡面所說的都懂,但是實際用到專案時就完全不懂,真的要多多練習,藉由練習才能知道自己了解多少 與吸收多少。

 

以上

3 則留言:

  1. 我在實作多層架構時,總是覺得Repository層有點多餘。
    因為使用ORM(entity framework),其實已經把實際的資料存取與邏輯隔離開來了。
    就算需要換資料庫類型,只要Provider有提供,也都能抽換。
    所以我後來做專案,都是保留Service層,但省略Repository層。
    專案還是分為 Repository <- Service,但Repository只放了Context與Model等Entity物件,
    實際上比較像是 Models <- BLL 這樣的感覺。

    不過有個專案,客戶有個很奇怪的要求(而且還是開發途中才說...)
    「我們不懂ORM,要在專案裡面看到所有SQL Statement,否則就退」
    已經用ORM做到幾乎收尾部份了...

    這時Repository這層的價值就出來了,
    雖然很困擾,不過還是用SQL Statement去塞滿了Repository這層,等於自己實作了部分的ORM,
    說真的算是規模很大的refactoring,但還好專案的其他部分(尤其是前端)不用跟著修改。
    不過這個專案算是特例。

    回覆刪除
    回覆
    1. 看起來你應該沒有做單元測試,
      不管有沒有做單元測試,要不要使用 Repository 以及架構如何劃分,其實就看實務需求來決定,
      在大型企業或是喘統產業裡,要能夠使用 ORM 的機會不多,除非是新專案而且沒有包袱,
      否則資料存取層還是會有,至於叫什麼名字都不打緊,主要就是資料存取的工作,
      這一層也不見得會有抽換資料庫或是其他資料存取方式的需求,
      但有了這一層,在大型架構以及要求嚴謹的軟體開發,做單元測試或是整合測試時,就會需要 Repository 的存在,
      品質要求是相當重要的。

      刪除

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。