網頁

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 的存在,
      品質要求是相當重要的。

      刪除