Model-View-Controller(模型-視圖-控制器,MVC) 模式將你的軟件組織並分解成三個截然不同的角色:
Model 封裝了你的應用數據、應用流程和業務邏輯。
View 從 Model 獲取數據並格式化數據以進行顯示。
Controller 控制程序流程,接收輸入,並把它們傳遞給 Model 和 View。
與其它設計模式不同,MVC 模式並沒有直接反映一個你能夠編寫或配置的類結構。相反,MVC 更像一個概念上的指導原則或范型。概念上的 MVC 模式被描述為三個對象 —— Model、View 和 Controller —— 之間的關系。由於 View 和 Controller 都可以從 Model 請求數據,所以 Controller 和 View 都依賴 Model。任何輸入都通過 Controller 進入你的系統,然後 Controller 選擇一個 View 來發出結果。
Model 包含了你的應用邏輯和數據,在你的應用程序中,它很可能是主要的值驅動器。Model 沒有任何與表現層相關的特性,而且也和 HTTP 請求處理職責中完全無關。
Domain Model 是一個對象層,是對現實世界邏輯、數據和你應用程序所處理的問題的抽象。Domain Model 可分為兩大類:Simple Domain Model 和 Rich Domain Model。
Simple Domain Model 往往是業務對象和數據庫表之間一對一的通信。你已經見過的幾種模式 —— Active Record、Table Data Gateway,以及 Data Mapper,所有這些與數據庫相關的設計模式 —— 可以幫助你把與數據庫相關的邏輯組織成一個 Domain Model。
Rich Domain Model 包含復雜的,使用繼承機制緊密聯系在一起的對象網絡,在本書和 GoF 一書中介紹的眾多模式起著槓桿作用。Rich Domain Models 往往是柔性的,精心測試過的,不斷重構的,而且與它們所表達的領域所需的業務邏輯緊密耦合。
采用哪種 Domain Model 類型取決於你的應用環境。如果你正在建立的是一個非常簡單的表單處理 web 應用,沒必要建立 Rich Domain Model。然而,如果你正在編寫一個價值數百萬的企業內聯網架構的核心庫,那麼努力開發一個 Rich Domain Model 就是值得的,它可以為你提供一個准確表達業務過程的平台,並可以讓你快速傳輸數據。
Martin Fowler 在 PoEAA 中同時簡要介紹了兩種 Domain Model。而 Eric Evans 的 Domain Driven Design 一書,則完全專注於 Rich Domain Model 的實踐應用和開發過程。
View 用於處理所有表現層方面的問題。View 從 Model 獲取數據,並可以把它格式化成用於 web 頁的 HTML,用於 web 服務的 XML,或用於 email 的文本。
許多的MVC模式的實現也都使用一個View Model或Application Model的概念,Controller是溝通的媒介,架起領域模型和用戶界面之間的橋梁,屬於表現層。為了View的簡單性,Controller負責處理或者將領域模型轉換成一個View Model,這通常叫做數據傳輸對象(DTO)。
<譯>12個asp.net MVC最佳實踐針對Model的最佳實踐有這麼一段:
7–DomainModel != ViewModel
DomainModel代表著相應的域,但ViewModel卻是為View的需要而創建。這兩者之間或許(一般情況下都)是不同的,此外DomainModel是數據加上行為的組合體,是由復雜的變量類型組成的並且具有層次。而ViewModel只是由一些String等簡單變量類型組成。如果想移除冗余並且容易導致出錯的ORM代碼,可以使用AutoMapper.如果想要了解更多,我推薦閱讀:ASP.NET MVC View Model Patterns.
那麼領域模型(Domain Model )和視圖模型(View Model)有什麼不同呢?
在ASP.NET MVC的應用程序中經常可以可以看到View Model,經常我們都認為領域模型和視圖模型是同一個東西。這特別是把領域模型包含在數據傳輸對象DTO裡的時候,例如使用Entity Framework之類的ORM工具生成的實體。在這種情況下,領域模型和視圖模型包含的實體非常相似,都是一些簡單的CRUD操作。
這些實體有許多屬性,有相同或類似的名稱,你可以很容易地映射領域實體對應視圖模型中的一個屬性。不過,這些相似的屬性也可能略有不同,例如類型或者格式。例如,用戶填寫的用戶界面的一個屬性,他在視圖模型裡可能是一個“Nullable”的。另一方面,領域實體可能需要一個經過驗證的合法的值,所以需要一個在用戶界面的領域模型之間的轉換。另一個例子是,用戶界面可能會顯示一個滑塊,用於用戶選擇多少天以後提交他的訂單。在這種情況下,視圖模型可能使用一個整數屬性來表示,領域模型通常是一個日期值。
視圖模型通常只包含領域模型的一個子集,而且只包含界面上所需要的屬性。此外,視圖模型可能是一個領域模型樹的扁平版本,例如,一個 Customer實體有一個Address,而這又是一個整體,它包含街道地址,郵政編碼,國家等。一個Customer 視圖模型用於顯示數據,將地址數據拉平填充到視圖模型類裡。
此外如果一個View需要同時處理幾個領域模型,View Model就是這幾個Domain Model的總和。領域模型和視圖模型之間有很多相似的地方,我們經常干脆就把Domain Model當作View Model來使用了。
上面討論了領域模型和視圖模型的相似性,我們來看看都有幾種方式把領域模型轉換為視圖模型,通常有3種方法:
把領域模型當作視圖模型來用,也就是領域模型就是視圖模型,大部分都是這麼用的。
視圖模型裡面包含一個領域模型,定義一個視圖模型,裡面包含了一個領域模型,通過屬性方式進行訪問。
將領域模型映射到視圖模型,領域模型並沒有直接映射到視圖模型,需要處理這種映射關系。
我們不建議直接把領域模型實體暴露給視圖,因為有許多細微之處,可能導致您混合業務和表示層的邏輯,無論是領域實體的屬性顯示還是業務的驗證規則,這都是應用程序處理的不同方面。直接將你的領域模型作為Conroller上的處理參數面臨著安全風險,因為Controller或者Model binder必須確保屬性驗證和用戶不能修改她自己不能修改的屬性(例如,用戶手動更新了一個隱藏的輸入值,或增加一個額外的屬性值,而這個並不是界面上的元素,但卻正好領域模型實體的屬性,這種風險叫做“over-posting”),即使對當前版本的領域模型做了正確的驗證,領域模型將來可能做了變更修改,並沒有出現編譯錯誤或者警告,可能導致新的風險。
我們應當避免使用前兩種方法將領域模型轉換成視圖模型,推薦使用第三種方法,定義單獨的視圖模型類。做這種領域模型到視圖模型的轉換工作是一種重復性的工作,已經有幾個工具可以幫助你來完成這項工作。最常用的一個工具就是.NET 社區的開源項目AutoMapper。