关键的区别是在 Sanderson 的图表中包含了“表示模型”。ASP.NET MVC 实现经常使用模型作为表示模型,即使它们可以不同(我认为它们应该不同,但这是一场圣战,没有必要深入探讨)。
在大多数非常简单的 ASP.NET MVC 应用程序中,模型就是数据实体。无论是 EF 实体还是 Linq2Sql 实体,都没有区别。这是因为大多数应用程序都是简单的表单数据,并且表示可能与持久性是一对一的。
然而,MVC 模式本身并不需要这个。在更纯粹的框架无关形式中,Sanderson 的图表说明了控制器正在与模型交互的事实。从这个意义上说,该模型实际上是“领域核心的门户”。控制器和视图是应用程序的一部分,但模型具有底层业务逻辑,在此之下还有持久层和其他应用程序未知的基础设施信息(当然是适当分离的)。控制器和模型之间的边界是应用程序边界,其他应用程序也可以在该点连接到域核心并与之交互。
表示模型通常只是一个简单的值对象。它不是任何类型的实体,因为它不必像可持久化的业务实体那样展示任何业务行为或维护其生命周期。它只是一个具有某些数据属性的平面对象。
它可以有一些行为,但这种行为是针对应用程序的,而不是针对域核心的。例如,也许它有一些视图可以使用的方法或属性。表示模型是应用程序的一部分,因此它是表示层感知的。本质上,它只是保存控制器需要传递给视图的数据(甚至从请求中接收,具体取决于框架)。
在 ASP.NET MVC 中,您会经常看到该模型也用作表示模型。在这些情况下,同一个对象可能扮演两个角色,但这两个角色肯定是不同的。
编辑:刚刚注意到你更新的问题......
在那个例子中,Album
扮演着领域模型和表示模型的角色。(事实上,我认为它根本不是领域模型,因为它太贫乏了。请注意,它没有功能,只有裸数据。)在更丰富的领域模型中,Album
可能会有更多的功能。对于一个人为的例子,想象一下它不是自动实现的属性,而是在设置时强制执行业务逻辑的属性,并且它具有诸如AddSong(Song song)
和Play()
其他此类行为之类的方法。
这种更丰富的模型仍然可以用作表示模型,但该功能在视图范围内可能没有意义。视图确实更适合裸数据元素。控制器将与模型的功能交互。因此,您可能会创建一个在结构上类似于Album
域模型的表示模型,它看起来就像您的示例中的那个。
展望未来,如果视图还需要其他数据怎么办?也许视图需要了解与Album
. 修改域模型以适应视图是没有意义的。那是倒退。演示文稿应该围绕领域核心,而不是相反。因此,您可以向表示模型添加属性,这些属性是从控制器内部的其他事物中填充的。
所以你最终可能会得到这样的东西......
领域模型:
public class Album
{
public int ID { get; private set; } // might need to be immutable
private string _title;
public string Title
{
get { return _title; }
set
{
// don't allow empty titles
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException("Title");
_title = value;
}
}
private Album() { }
public Album(int id, string title)
{
ID = id;
Title = title;
}
public void Play()
{
// some implementation
}
public void SomeOtherMethod()
{
// some implementation
}
}
随着业务领域的增长和变化,这个模型可能会随之改变。要点是它根据领域核心和业务逻辑的要求进行更改,而不是根据 UI 实现的要求进行更改。
使用此域核心的特定网站上的特定“页面”可能需要有关专辑的特定信息,并且可能还需要一些其他信息。您将定制一个演示模型以适应它:
public class AlbumViewModel
{
public int ID { get; set; }
public string Title { get; set; }
public string Owner { get; set; }
public IEnumerable<Listener> Listeners { get; set; }
public string SomeCompletelyUnrelatedValueNeededByTheView { get; set; }
}
然后控制器将为视图构建这个表示模型:
public ActionResult Details(int id)
{
// TODO: validate and sanitize any inputs
var album = AlbumRepository.Get(id); // after all, why bind the UI _directly_ to the DB? that's just silly
var someOtherObject = SomeOtherRepository.Get(someOtherValueFromSomewhereElse);
var albumVM = new AlbumViewModel
{
ID = album.ID,
Title = album.Title,
Owner = somethingElse.SomeValue,
Listeners = someOtherObject.GetListeners(album),
SomeCompletelyUnrelatedValueNeededByTheView = "foo"
};
return View(albumVM);
}
总体而言,这是一种更加手动的方法。当您有更复杂的域模型、与该域交互的多个复杂应用程序、整个域中的不同技术堆栈等时,它很有用。对于简单的表单数据应用程序,标准的 ASP.NET MVC 实现通常可以正常工作。它的大多数教程都反映了这一点,将多个职责合并到更少的对象中,使用装饰器而不是显式代码(假设全面使用相同的工具堆栈)等。
您正在查看的示例可以让您使用很少的代码非常快速地获得一个工作应用程序。与任何框架一样,如果您按照框架希望您做的方式做事,它就会很好地工作。如果您需要跳出框架的界限,您仍然可以以更抽象和与框架无关的方式维护该模式。
编辑:再次为您的更新...
在某种程度上,是的。ASP.NET MVC 是一个从总体上借鉴了 MVC 模式的框架。与所有事情一样,有不止一种方法可以做到这一点。坚持简单的实现和快速的应用程序,由 ASP.NET MVC 框架提供并在其各种教程中解释的功能是完全可以接受的,并且是使用框架的一个很好的例子......使用工具完成工作。
他们以所有最有意义的方式坚持这种模式。然而,与此同时,在框架的真实性质(通常超出模式描述的范围)中,它们试图为您提供使实际开发工作变得非常轻松的工具。如果您没有迫切需要将域模型与表示模型分开,则不必这样做。一个模型可以同时扮演这两个角色。如果您没有迫切需要将数据访问抽象为存储库模式,那么您不必这样做。您可以直接在模型中组合一些快速的实体框架功能并使用它来完成。
最终取决于项目的需求、开发人员的偏好等等。模式更学术,框架更实用。平衡两者是关键。