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