ASP.NET MVC中使用View Model分离领域模型

flywaterfree 2010-04-09

MVC架构模式是近年来编程世界里最长被提及的模式之一,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可分为两大类:SimpleDomainModel和RichDomainModel。

SimpleDomainModel往往是业务对象和数据库表之间一对一的通信。你已经见过的几种模式——Active Record、Table Data Gateway,以及DataMapper,所有这些与数据库相关的设计模式——可以帮助你把与数据库相关的逻辑组织成一个Domain Model。

RichDomain Model包含复杂的,使用继承机制紧密联系在一起的对象网络,在本书和GoF一书中介绍的众多模式起着杠杆作用。Rich Domain Models往往是柔性的,精心测试过的,不断重构的,而且与它们所表达的领域所需的业务逻辑紧密耦合。

采用哪种Domain Model类型取决于你的应用环境。如果你正在建立的是一个非常简单的表单处理web应用,没必要建立RichDomain Model。然而,如果你正在编写一个价值数百万的企业内联网架构的核心库,那么努力开发一个RichDomain Model就是值得的,它可以为你提供一个准确表达业务过程的平台,并可以让你快速传输数据。

MartinFowler在PoEAA中同时简要介绍了两种Domain Model。而EricEvans的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!=View Model

Domain Model代表着相应的域,但View Model却是为View的需要而创建。这两者之间或许(一般情况下都)是不同的,此外Domain Model是数据加上行为的组合体,是由复杂的变量类型组成的并且具有层次。而ViewModel只是由一些String等简单变量类型组成。如果想移除冗余并且容易导致出错的ORM代码,可以使用AutoMapper.如果想要了解更多,我推荐阅读:ASP.NET MVCView ModelPatterns,那么领域模型(Domain Model)和视图模型(View Model)有什么不同呢?

在ASP.NETMVC的应用程序中经常可以可以看到View Model,经常我们都认为领域模型和视图模型是同一个东西。这特别是把领域模型包含在数据传输对象DTO里的时候,例如使用EntityFramework之类的ORM工具生成的实体。在这种情况下,领域模型和视图模型包含的实体非常相似,都是一些简单的CRUD操作。

这些实体有许多属性,有相同或类似的名称,你可以很容易地映射领域实体对应视图模型中的一个属性。不过,这些相似的属性也可能略有不同,例如类型或者格式。例如,用户填写的用户界面的一个属性,他在视图模型里可能是一个“Nullable”的。

另一方面,领域实体可能需要一个经过验证的合法的值,所以需要一个在用户界面的领域模型之间的转换。另一个例子是,用户界面可能会显示一个滑块,用于用户选择多少天以后提交他的订单。在这种情况下,视图模型可能使用一个整数属性来表示,领域模型通常是一个日期值。

视图模型通常只包含领域模型的一个子集,而且只包含界面上所需要的属性。此外,视图模型可能是一个领域模型树的扁平版本,例如,一个Customer实体有一个Address,而这又是一个整体,它包含街道地址,邮政编码,国家等。一个Customer视图模型用于显示数据,将地址数据拉平填充到视图模型类里。

此外如果一个View需要同时处理几个领域模型,View Model就是这几个DomainModel的总和。领域模型和视图模型之间有很多相似的地方,我们经常干脆就把DomainModel当作View Model来使用了。上面讨论了领域模型和视图模型的相似性,我们来看看都有几种方式把领域模型转换为视图模型,通常有3种方法:

1.把领域模型当作视图模型来用,也就是领域模型就是视图模型,大部分都是这么用的。

2.视图模型里面包含一个领域模型,定义一个视图模型,里面包含了一个领域模型,通过属性方式进行访问。

3.将领域模型映射到视图模型,领域模型并没有直接映射到视图模型,需要处理这种映射关系。

我们不建议直接把领域模型实体暴露给视图,因为有许多细微之处,可能导致您混合业务和表示层的逻辑,无论是领域实体的属性显示还是业务的验证规则,这都是应用程序处理的不同方面。

直接将你的领域模型作为Conroller上的处理参数面临着安全风险,因为Controller或者Modelbinder必须确保属性验证和用户不能修改她自己不能修改的属性(例如,用户手动更新了一个隐藏的输入值,或增加一个额外的属性值,而这个并不是界面上的元素,但却正好领域模型实体的属性,这种风险叫做“over-posting”),即使对当前版本的领域模型做了正确的验证,领域模型将来可能做了变更修改,并没有出现编译错误或者警告,可能导致新的风险。

我们应当避免使用前两种方法将领域模型转换成视图模型,推荐使用第三种方法,定义单独的视图模型类。做这种领域模型到视图模型的转换工作是一种重复性的工作,已经有几个工具可以帮助你来完成这项工作。最常用的一个工具就是.NET社区的开源项目AutoMapper。

相关推荐