天天看點

一起談.NET技術,使用View Model從表現層分離領域模型

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)。

7–DomainModel != ViewModel

  那麼領域模型(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”),即使對目前版本的領域模型做了正确的驗證,領域模型将來可能做了變更修改,并沒有出現編譯錯誤或者警告,可能導緻新的風險。

      如何使用AutoMapper可以參考下面的兩篇文章介紹: