87

首先,在有人大喊大叫之前,我很难用一个简单的标题来概括它。另一个标题可能是“领域模型和 MVC 模型有什么区别?” 或“什么是模型?”

从概念上讲,我将模型理解为视图和控制器使用的数据。除此之外,对于模型的构成似乎有很多不同的意见。什么是域模型、应用程序模型、视图模型、服务模型等等。

例如,在我最近询问存储库模式的一个问题中,有人直接告诉我存储库是模型的一部分。但是,我阅读了其他意见,认为模型应该与持久性模型和业务逻辑层分开。毕竟,存储库模式不应该将具体的持久性方法与模型分离吗?其他人说域模型和MVC模型之间存在差异。

让我们举一个简单的例子。MVC 默认项目中包含的 AccountController。我已经阅读了一些意见,其中包含的帐户代码设计不佳,违反了 SRP 等等。如果要为 MVC 应用程序设计一个“适当的”成员资格模型,那会是什么?

您将如何将 ASP.NET 服务(成员资格提供程序、角色提供程序等)从模型中分离出来?或者你会吗?

在我看来,模型应该是“纯粹的”,可能带有验证逻辑..但应该与业务规则分开(验证除外)。例如,假设您有一条业务规则,规定在创建新帐户时必须向某人发送电子邮件。在我看来,这并不真正属于模型。那么它属于哪里呢?

有人愿意解释这个问题吗?

4

4 回答 4

69

我这样做的方式——我并不是说这是对还是错,是拥有我的观点,然后是一个适用于我观点的模型。该模型仅包含与我的视图相关的内容 - 包括数据注释和验证规则。控制器仅包含用于构建模型的逻辑。我有一个包含所有业务逻辑的服务层。我的控制器调用我的服务层。除此之外是我的存储库层。

我的域对象是分开存放的(实际上是在他们自己的项目中)。他们有自己的数据注释和验证规则。我的存储库会先验证域中的对象,然后再将它们保存到数据库中。因为我的域中的每个对象都继承自内置验证的基类,所以我的存储库是通用的并验证所有内容(并要求它从基类继承)。

你可能会认为拥有两组模型是代码重复,并且在某种程度上是重复的。但是,在某些完全合理的情况下,域对象不适合视图。

一个典型的例子是使用信用卡时 - 我在处理付款时需要一个 cvv,但我无法存储 cvv(这样做会被罚款 50,000 美元)。但是,我还希望您能够编辑您的信用卡 - 更改地址、姓名或到期日期。但你不会在编辑时给我号码或 cvv,我当然不会把你的信用卡号码以纯文本形式放在页面上。我的域具有保存新信用卡所需的这些值,因为您将它们提供给我,但我的编辑模型甚至不包括卡号或 cvv。

这么多层的另一个好处是,如果架构正确,您可以使用结构映射或其他 IoC 容器并交换部分,而不会对您的应用程序产生不利影响。

在我看来,控制器代码应该只是针对视图的代码。显示这个,隐藏那个,等等。服务层应该包含应用程序的业务逻辑。我喜欢将所有这些都放在一个地方,以便轻松更改或调整业务规则。存储库层应该相对笨拙——没有业务逻辑,只查询您的数据并返回您的域对象。通过将视图模型与域模型分离,您在自定义验证规则方面拥有更大的灵活性。这也意味着您不必将每条数据都转储到隐藏字段中的视图中,并在客户端和服务器之间来回推送(或在后端重新构建)。

<% if (!String.IsNullOrEmpty(Model.SomeObject.SomeProperty) && 
    Model.SomeObject.SomeInt == 3 && ...) { %>

虽然一切似乎都分散并分层,但它有一个以这种方式构建的目的。完美吗?并不真地。但我确实更喜欢它而不是过去从控制器调用存储库并将业务逻辑混合在控制器、存储库和模型中的设计。

于 2010-12-30T20:08:00.777 回答
17

我经常想知道 MVC 元素究竟如何适合传统的 Web 应用程序结构,在这种结构中,您拥有视图(页面)、控制器、服务和数据对象(模型)。正如你所说,有很多版本。

我认为存在混淆是因为上述广泛接受的架构,它使用“贫血域模型”(据称)-反模式。我不会详细介绍贫血数据模型的“反模式”(您可以查看我在此处解释事物的努力(基于 Java,但与任何语言相关))。但简而言之,这意味着我们的模型只保存数据,业务逻辑放在服务/管理器中。

但是让我们假设我们有域驱动架构,并且我们的域对象是它们所期望的方式——同时具有状态和业务逻辑。在这个领域驱动的视角下,事情就到位了:

  • 视图是 UI
  • 控制器收集 UI 的输入,调用模型上的方法,并将响应发送回 UI
  • 模型是我们的业务组件——保存数据,但也有业务逻辑。

我想这回答了你的主要问题。当我们添加更多层时,事情会变得复杂,比如存储库层。通常建议它应该由放置在模型中的业务逻辑调用(因此每个域对象都有对存储库的引用)。在我链接的文章中,我认为这不是最佳实践。事实上,拥有一个服务层并不是一件坏事。顺便说一句,领域驱动设计不排除服务层,但它应该是“瘦”的,并且只协调领域对象(因此没有业务逻辑)。

对于被广泛采用(无论好坏)的贫血数据模型范式,该模型既是服务层又是您的数据对象。

于 2010-12-30T20:07:45.253 回答
3

在我看来,

模型 -

不应包含业务逻辑,它应该是可插拔的(WCF 类场景)。它用于绑定到视图,所以它应该具有属性。

商业逻辑 -

它应该放在“域服务层”,它完全是单独的层。此外,将在此处添加一层“应用程序服务”。

应用服务与域服务层对话以应用业务逻辑,然后最后返回模型。

所以,控制器会向应用服务询问模型,流程会像这样,

    Controller->Application Services(using domain services)->Model
于 2010-12-30T20:14:33.117 回答
2

MVC 模式和 Asp.net 框架对模型应该是什么没有区别。

MS 自己的示例包括模型中的持久性类。您关于模型中的成员资格的问题。这取决于。模型中的类是否归某物所有?谁登录和显示什么数据之间有联系吗?是否过滤了可编辑的权限系统的数据部分?谁最后更新或编辑了您域的对象部分,因为其他人需要查看它或后端支持的东西?

电子邮件示例也取决于。您是否熟悉域事件或特别是事件?您有单独的服务来发送电子邮件吗?发送电子邮件的行为是您域的一部分,还是您系统范围之外的应用程序级别问题?UI 是否需要知道电子邮件是否发送成功?发送失败的邮件是否需要重试?是否需要存储发送的电子邮件内容以满足支持或客户服务要求?

这些类型的问题过于宽泛和主观,但我正在回答,所以你和投票给你的每个人都可以理解这一点。

您的需求/时间表/资源都融入了您的系统架构。甚至收入模式也会产生影响。您还必须考虑要拍摄的模式。DDD 与持久化模型应用程序有很大不同,两者之间的所有差异也适用于某些应用程序。您是在为测试应用程序而拍摄吗?所有这些都会产生影响。

于 2010-12-30T20:09:22.170 回答