MVC:在哪里放置业务逻辑?[关闭]


81

首先,我已经看到了很多有关此问题,但是背后的原因却不够多。如果我的问题不够好,应该删除,我会理解的。

例如,我看过这个问题,有超过45个赞成票的答案说他建议您将业务逻辑放入模型中,这听起来很合逻辑。

但是,我的第一个大型项目已经在控制器中完全使用了所有BL,因为我没有对这些事情提出疑问,而是看一下如何完成操作(AccountController如果您选择带有表单身份验证的MVC,该操作会自动添加)。所有方法看起来都充满了BL。还是可能添加的最少代码量让我无所适从?

youtube上的一个人通过将所有逻辑放到模型中来问我他是否正确,起初我不是!然后我开始认为也许他是对的!?

那么,毕竟我应该把业务逻辑放在哪里?如果在模型类中,那么在控制器中的方法中应该考虑多少代码是正常数量?一行最多只能在控制器中的模型中调用某种方法,然后返回到视图?


1
业务逻辑进入控制器。模型逻辑包含在模型中。模型逻辑是专门/仅处理模型的事物。设置器/获取器/属性/添加器/删除器等
粉碎

1
@暗恋:我不同意。正如我读过的:“一个模型对象保存了应用程序的数据和“业务逻辑。”和“控制器对象将模型和视图对象联系在一起。”
ChiefTwoPencils 2013年

@BobbyDigital-您可以提供到源的链接吗?:)
AndriusNaruševičius

当然,这是《大书呆子牧场指南》中正确使用MVC的说明,但不幸的是,您必须购买该书才能确认这一点。
ChiefTwoPencils 2013年

不过,我会说,刚刚尝试在C#书中对此进行确认,在asp.net中,业务逻辑进入中间层,其中顶层是UI,中间是控制器,底层是db。但是他们并没有特别/明确地谈论MVC。这来自于C#for Programmers :)
ChiefTwoPencils

Answers:


54

由于两个原因,我倾向于将域逻辑放入模型中。

  1. 该模型中应该没有UI代码,因此更易于测试。只要有可能,我都希望在编写任何UI代码之前拥有一个可以正常工作(意味着完整的测试覆盖范围)的模型。控制器可以相信模型在做正确的事情,并且只处理UI问题。

  2. 如果将域逻辑放在控制器中,则在不同应用之间甚至不同控制器之间共享并不容易。


2
是的,我非常喜欢,#2因为我发现自己很难在控制器之间共享(不得不使用静态方法)!
AndriusNaruševičius13年

是的@AndriusNaruševičius,不要那样做。您应该尝试将依赖项注入控制器中,而不要依赖于其他控制器。
Mark Walsh

请注意,我认为此答案是关于“域模型”(经典MVC模式的M部分)的,这与ASP.Net MVC“模型”(MVVM模式的一部分)无关。
Alexei Levenkov 2014年

1
那么...您将依赖多个模型的逻辑放在哪里?
Kolob Canyon

1
@Ferrucio那是一个不好的解决方案。您最终会遇到“依赖地狱”,在其中创建依赖于要创建的其他对象的对象。这是一个糟糕的解决方案,因为不必要的/未使用的数据会无故传递。您希望函数只执行完成任务所需的内容-否则代码很快就会变得不清楚。更好的解决方案是创建仅需最少构建的业务逻辑类(甚至更好的是,使用依赖注入来构建)。这样,您永远不必最终获取几个不相关的对象来执行业务逻辑。
Kolob Canyon

43

我喜欢保持模型整洁,即仅具有属性而没有业务逻辑。我一直认为将依赖项注入到控制器中是很好的,这些依赖项包含了我在模型上执行的逻辑。我喜欢在可能的情况下坚持单一责任原则,并且发现使用大量方法的模型会很快膨胀。两者都有优点和缺点,注入很多依赖项会产生开销,但允许隔离测试并保持类简单,最终您将拥有精简的控制器。尽管我的逻辑实际上不是作为班级成员存在于模型中的,但它仍然是业务逻辑。我倾向于在控制器中没有定义业务逻辑,因为像Httpcontext这样的嘲笑之类的东西有些噩梦,而且是不必要的。


5
+1完全同意。我以为我是唯一一个将模型责任降到最低的人!

1
当然,他是我的登录控制器gist
Mark Walsh

1
因此,如果您不将逻辑放入模型或控制器中,那么将其放在哪里?
niico

1
如何将参数传递给控制器​​的构造函数?控制器的初始化通常不是在幕后完成吗?
杰夫

1
我使用依赖注入。
马克·沃尔什

23

业务逻辑属于问题域,一切都属于问题域进入到模型中MVC。

控制器应负责从模型传递数据到视图和从视图回到模型。因此,控制器是用户与之交互以及程序如何建模和存储问题状态之间的桥梁。该管道,可以这么说。

这里的关键是业务逻辑和管道逻辑之间的区别。在我看来,自动生成的帐户控制器的工作主要是管道,而不是真正的业务逻辑。请记住,管道逻辑不一定完全短,因此您无需施加人为的限制(例如“控制器中最多X个呼叫数”)。


我同意所有这些。但是,我认为很多困惑来自如何在模型中构造类,尤其是在EF中。IE:您是否使用部分类并在不同的C#文件中构建逻辑?一个文件用于EF,一个文件用于逻辑?
S1r-Lanzelot

13

我的团队从webforms(asp.net)转到mvc时,进行了大量研究,并提出了以下结构。据我说,它与应用程序的大小无关。关于保持代码干净清晰。

DAL项目

AccountsDAL.cs --- > Calls SP or any ORM if ur using any

BLL项目

AccountsBLL.cs ---> Calls DAL

网络项目

Model
    AccountsModel --- > Contains properties And call BLL
Controllers
    IndexController ---> Calls Models and returns View
Views
    Index

控制器应负责模型和视图之间的数据传递。除此之外,不应有任何不必要的代码。例如,如果要记录日志,则应在模型级别而不是控制器上完成。


13

关于这个话题似乎有些困惑。大多数情况下,人们似乎倾向于将MVC模式与N层架构相混淆。现实情况是两种方法可以一起使用,但是一种方法不依赖于另一种方法,并且都不是必需的。

N层体系结构与将应用程序分成多个层有关。一个简单的示例是将应用程序划分为表示层,业务逻辑层和数据访问层。

MVC是一种处理应用程序表示层的设计模式。完全有可能按照MVC方法设计应用程序,而无需将业务逻辑和数据访问逻辑与表示层分开,从而最终得到单层设计。

结果,如果您遵循MVC方法而不将应用程序划分为多个层,则最终将导致模型,视图和控制器将业务规则和数据访问逻辑与其他逻辑混合在一起。

根据定义,在N层体系结构中,表示层仅应能够与业务逻辑层进行通信,因此应确保任何MVC组件只能与业务逻辑层进行通信。

如果您要构建的应用程序不涉及表示,因此不涉及表示层,则不必担心MVC模式。但是,即使不涉及表示层,您也可能仍然可以将应用程序划分为多个层,从而遵循N层设计。


8

一般来说,业务逻辑不应驻留在任何MVC播放器中;它只能由您的控制器操作消耗

正如许多人提到的,最好创建一个库来托管业务逻辑,作为一组与客户无关的可重用组件。

通过这种方式,我们可以大大提高软件的可重用性,兼容性,可伸缩性和可测试性。我们还减少了对某些框架功能的依赖,这使向新/不同技术的移植变得更加容易。

多年来,将我们的业务逻辑抽象为一个独立的程序集(一个或多个程序集)一直对我们有好处。然后,几乎任何.NET技术(ASP.NET MVC / API / Core,WPF,Win Forms,WCF,UWP,WF,控制台等)都可以使用我们的业务逻辑。

此外,我们希望中间层能够处理业务规则和验证逻辑,以减少对.NET MVC框架的依赖。例如,我们避免使用.NET MVC验证帮助程序,而是依靠我们自己的帮助程序。这是另一个因素,使我们可以轻松使用任何.NET技术的业务逻辑。

通过这种方式在逻辑上设计我们的中间层,使我们能够轻松实现以下物理体系结构:

在此处输入图片说明

它是用Peasy.NET编写的,多年来为我们提供了很好的服务。事实上,我们决定将其开源。

如果有人对我们的中间层是什么样子感到好奇,那么这里有一个与客户无关的业务层示例。它还展示了多个.NET客户端(ASP.NET MVC,Web Api和WPF)的使用情况。

希望这对某人有帮助!


大多数情况下,无需重复使用逻辑。如果我说决定将ASP.NET Core MVC用于Web API,我将永远不想在WPF或WinForms中使用该业务逻辑。因为客户端仍然会与服务器通信。将业务逻辑(尤其是数据库访问逻辑)放在客户端很不好。
康拉德

您添加的层越多,我说它会降低可维护性和可测试性。最终,集成测试更重要。
康拉德

8

业务逻辑不应进入模型视图或控制器。应该有一个单独的业务逻辑层;该层的唯一目的是处理您的业务逻辑。这与SOLID更加一致。

如果将业务逻辑放在MV或C中,则最终会导致难以测试/重用的代码。

将逻辑放入模型怎么样?

那是一个不好的解决方案。

您将最终陷入依赖地狱,其中对象依赖对象。 在此处输入图片说明

即使您拥有简单的简单函数,您仍然必须满足所有依赖关系才能调用它。

这也将导致不必要的未使用的数据传递周围没有任何理由。这也可能会影响性能,具体取决于性能的恶化程度。

我还应该提到,单元测试在a **中变得很痛苦,因为您必须模拟多个对象才能测试一个简单的函数。

适用清洁守则的原则

  1. 类/函数只需要完成工作所需的内容。
  2. 函数应尽可能包含3个参数或更少
  3. 智能地命名类/函数/变量(遵循Microsoft的标准)
  4. 不要将业务逻辑耦合到模型视图或控制器

控制器

在您的控制器中,您应该能够使用依赖注入来注入业务逻辑层。确保您的控制器仅用于将信息路由到业务逻辑层。控制器不应直接在其中包含业务逻辑。任何验证都需要IValidatable在模型上进行。任何业务逻辑都需要路由到单独的层。


在这里,我们有一个业务层,我只希望我们没有它。业务层是一团糟,逻辑应该在模型中。
Mateus Felipe

@MateusFelipe那么,您将需要多个模型的逻辑放在哪里(例如:付款和产品)?您是否创建一个具有PaymentProduct作为实例变量的新模型?你给那个物体起什么名字?如果视图中未使用模型,则不再是模型。它是单独层的一部分。理想情况下,您上的那堂课应该只从“付款”和“产品”中获取所需的信息,以完成工作。如果只需要productNameand和price,则应该只接受这两个参数,而不是整个对象。
Kolob Canyon

4

我得到的一般答案是业务逻辑通常分为两类:

面向对象的业务逻辑:获取建模为对象(在模型中),通常作为存储库注入。

程序业务逻辑:进入具有可注入控制器的接口的服务。

控制器逻辑:控制如何接收命令并将其传递给模型/服务,然后将这些结果传递给视图的逻辑。

控制器应该没有业务逻辑,这是设计模式中非常具体的部分,用于控制用户界面如何将输入传递给处理业务逻辑(或服务,如果问题本质上是过程性的)的模型。


2

我也喜欢保持模型清洁(参考:@Mark Walsh)。通过依赖注入可以很容易地解决无法重用控制器中嵌入的逻辑的问题,或者,如果您认为过多,可以通过接口公开您的业务/域逻辑,并在控制器中使用外观模式。这样一来,您可以获得所需的功能,但同时又要保持控制器和模型的美观和整洁。


1

我也希望保持模型清洁。MVC控制器应仅用于拨打电话,还应保持清洁。因此,根据其可重用性,敏感性和相关性,可以将业务逻辑编写为

1.WebApi控制器:使用webapi控制器的优点是您可以稍后将这些作为服务公开给其他设备,从而使您的代码可重复使用。

2. BAL /共同点:有些逻辑具有特定的用法,不能作为api公开,可以在此类中推送。

3.存储库:所有与数据库相关的查询都添加到存储库中。可以有一个通用存储库,它将为每个表实现所有功能(CRUD操作)或特定存储库。取决于要执行的操作。


1

正如ahanusa所写,您应该将业务逻辑放入单独的DLL或单独的目录中。
我经常在模型和控制器的同一级别使用名为Logics的目录,在该目录中放置执行业务逻辑的类。
这样,我让模型和控制器都清洁了。


0

我知道这是关于MVC的问题,但我认为我要举的示例(Web API)将很有用。

我正在开发我的第一个Web API,并且正在重用其他应用程序中的业务逻辑。具体来说,它来自外部DLL,因此我的API仅用于与SAP解决方案“对话”,接收来自PO的请求并将响应发送回去。

我如何将我的逻辑(已实现)放入控制器?我不需要 我的控制器将仅接收,验证请求并撰写响应以将数据发送回去。

我正在使用ViewModel类,它们必须具有的是一个映射函数,仅用于从TransferObjects中读取信息(来自外部DLL)并转换为ViewModel。

我对拥有业务逻辑的应用程序(在本例中为Web API)不满意,我认为可重用性将因此丢失。

我将业务逻辑视为我注入控制器的依赖项。

我在旧版中做了很多重构,以提供可单元测试的解决方案,我不得不创建许多接口,并在旧版中实现一些设计模式才能提供此解决方案。

在我看来,业务层必须位于应用程序之外,最好在另一个类库中。因此,您将在您的应用程序中实现一个真正的分离概念。

当然,如果您的CORE(业务)是您的应用程序(API / WebSite),则业务规则将实现到您的MVC类中。但是在将来,如果您要开发一个新的应用程序并且某些业务规则是相同的,那么我敢打赌,仅维护两个应用程序中实现的相同逻辑将有很多问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.