“业务逻辑层”在哪里适合MVC应用程序?

时间:2010-12-30 19:39:33

标签: asp.net-mvc model-view-controller design-patterns business-logic-layer

首先,在有人尖叫之前,我很难总结一个简单的标题。另一个标题可能是“域模型和MVC模型之间有什么区别?”或“什么是模特?”

从概念上讲,我理解一个Model是视图和控制器使用的数据。除此之外,关于构成模型的内容似乎存在很多不同的意见。什么是域模型,与应用模型,视图模型,服务模型等。

例如,在我最近提出的关于存储库模式的问题中,我被告知空白存储库是模型的一部分。但是,我已经阅读了其他意见,即模型应该从持久性模型和业务逻辑层中分离出来。毕竟,是不是存储库模式应该将具体的持久性方法与模型分离?其他人说Domain模型和MVC模型之间存在差异。

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

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

我看到它的方式,模型应该是“纯粹的”,也许是验证逻辑..但应该与业务规则分开(除了验证)。例如,假设您有一条业务规则,说明在创建新帐户时必须通过电子邮件发送某人。在我看来,这并不属于模型。那它属于哪里?

有人关心这个问题吗?

4 个答案:

答案 0 :(得分:69)

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

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

您可能认为拥有两组模型是代码重复,并且在一定程度上。但是,有完全合理的实例,域对象不适合视图。

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

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

在我看来,控制器代码应该只是针对视图的代码。显示此信息,隐藏该信息等。服务层应包含您应用的业务逻辑。我喜欢在一个地方拥有所有这些,因此很容易改变或调整业务规则。存储库层应该相对愚蠢 - 缺乏业务逻辑,只查询数据并返回域对象。通过将视图模型与域模型分离,您在自定义验证规则方面具有更大的灵活性。这也意味着您不必将所有数据转储到隐藏字段中的视图中,并在客户端和服务器之间来回推送(或在后端重建它)。然后,您的视图模型将仅包含与视图相关的信息 - 并且可以自定义视图逻辑或计数或枚举的bool,以便视图本身不会混杂复杂的逻辑语句,如

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

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

答案 1 :(得分:17)

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

我认为由于上述广泛接受的架构存在混淆,该架构使用“贫血域模型”(所谓的)抗性模式。我不会详细介绍贫血数据模型的“反模式”(你可以看一下我的努力来解释事物here(基于Java,但与任何语言相关))。但简而言之,这意味着我们的模型只包含数据,业务逻辑放在服务/经理中。

但是我们假设我们有domain driven architecture,我们的域对象就是它们的预期方式 - 具有状态和业务逻辑。在这个以域为导向的视角中,事情就会出现:

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

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

对于广泛采用的贫血数据模型范例(好的或坏的),模型既是服务层又是数据对象。

答案 2 :(得分:3)

在我看来,

模型 -

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

业务逻辑 -

它应该放在“域服务层”,它是一个单独的层。 此外,还将在此处添加一个“Application Services”层。

App Services与Domain Services层对话以应用业务逻辑,然后最后返回Model。

所以, 控制器将询问Application Service for Model,流程将如下,

    Controller->Application Services(using domain services)->Model

答案 3 :(得分:2)

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

MS自己的例子包括模型中的持久性类。关于会员资格在模型中的问题。这取决于。您的模型中的类是否归某些东西所有?登录的人与显示的数据之间是否存在链接?是否可以对可编辑权限系统的数据部分进行过滤?是谁最后一次更新或编辑了域名的对象部分,就像其他人需要查看它或其他东西以获得后端支持一样?

电子邮件示例也取决于它。您是否熟悉域名事件或事件?您是否有单独的服务来发送电子邮件?是您的域名发送电子邮件的行为还是超出系统范围的应用程序级别问题?用户界面是否需要知道电子邮件是否成功发送?无法发送的电子邮件是否需要重试?是否需要存储发送的电子邮件内容以获得支持或客户服务要求?

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

您的要求/时间表/资源都会渗透到您的系统架构中。即使revenue model也会产生影响。你还必须考虑你拍摄的模式。 DDD与持久性模型应用程序有很大不同,其间的所有slop对某些应用程序也有效。你正在拍摄测试应用程序吗?所有这些都有影响。