为什么要在模型中加入业务逻辑?当我有多种类型的存储时会怎样?


70

我一直认为业务逻辑必须在控制器中,并且控制器(因为它是“中间”部分)保持静态,并且模型/视图必须通过接口进行封装。这样,您可以在不影响任何其他因素的情况下更改业务逻辑,对多个模型(每个数据库/存储类型一个)进行编程,并为多个视图(例如针对不同的平台)编程。

现在,我在这个问题中读到,您应该始终将业务逻辑放入模型中,并且控制器与视图紧密相连。

对我来说,这没有任何意义,它意味着每次我想拥有一种支持另一种数据库/存储类型的方法时,都必须重写包括业务逻辑在内的整个模型。

如果我想要另一个视图,则必须重写视图和控制器。

有人可以解释为什么会这样,还是我在某个地方出错了?

Answers:


69

ElYusubov的答案主要是定论,领域逻辑应该进入模型,而应用程序逻辑应该进入控制器。

有两个澄清:

  • 这里的业务逻辑一词非常含糊,因为它是模棱两可的。业务逻辑是业务人员所关心的所有逻辑的总称,将其与单纯的技术(例如,如何在数据库中存储内容或如何在屏幕上呈现内容)区分开来。域逻辑(“有效的电子邮件地址看起来像...”)和工作流/业务流程(“用户注册时,索要其电子邮件地址”)都被视为业务逻辑,前者显然属于模型,后者是进入控制器的应用程序逻辑。
  • MVC是把屏幕上的东西,并允许用户与它进行交互的模式,它不指定存储在所有。大多数MVC框架都是完整的堆栈框架,不仅限于MVC,它还可以帮助您存储数据,而且由于通常应在模型中找到应存储的数据,因此这些框架为您提供了方便的方式来存储模型-数据库中的数据,但与MVC无关。理想情况下,模型应该与持久性无关,并且切换到其他类型的存储完全不应该影响模型代码。完整的体系结构具有一个持久层来处理此问题。

4
大多数MVC框架都将所有存储/数据库内容混入模型中,以便于存储模型(通常是通过扩展框架模型类来实现)。这可能是造成混乱的根源。从技术上讲,您编写的模型代码应该是实际的模型(域层),而框架提供的代码应该处理存储(持久层)。例如,诸如User.find(...)(其中User为模型)之类的东西可以工作,因为框架将存储库模式实现为Model的一部分。
Waquo 2012年

3
视图-控制器-模型-存储是一般原理(尽管M,V和C之间的关系应显示为三角形)。当您的框架将存储混合到其“模型”中时,它的工作原理如下:View-Controller-(模型从框架继承存储)。
Waquo 2012年

2
View-Controller-Model-Storage相当粗糙,因为它不应该是平坦的。例如,当控制器执行类似于User.find(...)的操作来获取模型时,它将直接询问存储层,而不是通过域层。
Waquo 2012年

2
在具有更仔细的分层的体系结构中,它将类似于UserRepository.find()。“模型”是指框架提供的“模型”类,您可以从该类继承。User.find()返回的用户对象是用户的模型,即有人建模用户是什么,用户的行为...
Waquo 2012年

1
@flipdoubt业务逻辑是任何从mvc移植为uwp应用程序时都应保持相同的逻辑。
安迪

23

您和编程界的大部分人似乎误解了MVC部分的作用。简而言之,它们是:

模型 =领域逻辑

视图 =输出逻辑

控制器 =输入逻辑

这意味着该模型负责整个业务逻辑:与在屏幕上绘制小部件,驱动打印机,将数据输出为HTML,解析HTTP请求等有关的任何事情都不属于模型。

但是,许多现代的所谓的“ MVC”框架实际上根本不执行MVC,否则会误标记其部件。通常,所谓的“模型”是模型的持久层,而业务逻辑位于他们所谓的“控制器”中。实际的控制器通常只是一个带有路由表的中央入口点,并在各个“控制器”中添加一些代码,以将收到的输入分配给正确的业务流程。这些框架所谓的“视图”实际上包含了所有内容:一些表示逻辑(视图),一些输入处理和验证(控制器)以及一些其他业务逻辑(模型)。通常,实际视图的大部分被称为“模板”。

您可能还想阅读有关多层体系结构的信息。MVC是一种单向的(流程是Controller-> Model-> View),Multi-Tier是一种双向的东西(Presentation-> Logic-> Data-> Logic-> Presentation),还有很多假装做MVC的框架实际上是三层的,重新标记了“从视图到视图”,“从逻辑到控制器,从数据到模型”的标签。


2
我相信您会错误地表示模型(“模型=领域逻辑”),在我看来,它更像是填充数据的容器,然后使用View以最纯粹的MVC模式呈现该数据。当然,在非常简单的用例中,您可以将其视为“域逻辑”,但我保证大多数有价值的系统都会很快增长。最好将“域逻辑”拆分为单独的层/类,例如服务层。
A. Murray

@ A.Murray:当然,模型不必是一个完整的代码块,将其分为持久性,数据结构和域逻辑通常很有意义。尽管如此,MVC在模型中将这三个问题归为一类。无论如何,当控制器和视图包含域逻辑时,它就不再是真正的MVC。
tdammers

@tdammers,我喜欢您回答的简洁性以及对逻辑的关注。您认为持久性和事务处理之类的应用程序关注点在哪里?似乎MVC应该是四个字母的缩写,例如MVCS,其中S用于服务。
flipdoubt

15

为了真正隔离业务逻辑并使之与表示层基础结构分离,应将其由应用程序服务封装。MVC体系结构是实现表示层的一种方法,它应该保留在该范围内,将所有业务逻辑委托给这些应用程序服务。将视图模型视为视图和需要在其上显示或读取的数据之间的适配器。控制器调解承载业务逻辑的视图模型,视图和应用程序服务之间的交互。

应用服务实现业务用例,并与表示层分离,无论是MVC还是其他。反过来,应用程序服务可以托管事务脚本域驱动的设计

对于存储,应用程序服务可以引用存储库或持久性机制的任何抽象。通过将数据访问抽象到一个接口中,可以支持不同的实现。通常,这些抽象是泄漏的,并且只能在实现中部分移植,这通常是徒劳的尝试,无法实现完全的可移植性。

更新

我的建议是基于六角形建筑。在六边形体系结构中,您的域模型(业务逻辑)是核心。此核心由充当外观的应用程序服务封装。应用程序服务是简单的类,具有与您域中的用例相对应的方法。有关应用程序服务的深入讨论,请查看域驱动设计中的服务。该代码示例包含a PurchaseOrderService,它是用于购买域的应用程序服务。(请注意,应用程序服务并不意味着使用域驱动的设计。)

在六边形体系结构中,MVC表示层是域模型(业务逻辑)和GUI之间的适配器。域模型不知道表示层,但是表示层知道域模型。

与将业务逻辑放置在控制器中的解决方案相比,该解决方案当然具有可移动的部分,您应该权衡弊端和好处。我之所以建议这样做,是因为我更喜欢使业务逻辑与表示层分离,以应对复杂性。随着应用程序的增长,这一点变得更加重要。


听起来您正在描述同时具有控制器和视图模型的MVC和MVVM混蛋。另外,我认为您要描述的体系结构对于OP的需求可能有点沉重。
Waquo 2012年

老实说,我更喜欢Waquo的回答。主要是因为我不知道您对“应用程序服务”的含义。你能解释一下这个词吗?我的GoogleFU在这里似乎无法正常工作。
斯蒂芬·温克勒

1
@Waquo我同意提议的体系结构可能会过大,但是应该考虑。我没有提到MVVM,它只是实现表示层的另一种方法。无论使用MVC还是MVVM,应用程序服务都适用,我建议的任何内容都不能表示两者的任何组合。
eulerfx 2012年

1

取决于您所指的业务逻辑。任何赋予模型内容含义的“逻辑”都应该在模型中。在链接的问题中,投票率最高的答案似乎将“业务逻辑”定义为与数据相关的任何事物;从企业的数据就是其业务的角度来看,这是有道理的!

我曾经看过Rails的创建者(我认为)的一个例子,他正在对此进行精确的介绍-不在模型中添加“业务逻辑”。他的示例是用于应用程序注册和登录的控制器类和方法-以明文形式提供的密码在插入模型或对模型(数据库)进行查询之前已经过加密。

我想不到一个更好的例子,它不是控制器逻辑,而是直接属于模型。

该模型可以作为无数数据存储的接口,从而减轻了可移植性问题。在这里,无论模型接口是否实际上是“控制器”,人们都可能会感到困惑。

一般来说,控制器链接模型和视图(这是应用程序的基本要素)。在Cocoa开发中,可以简化到通过XCode GUI(控制器对象和绑定)处理控制器的程度。

关于MVC的GoF的“设计模式”部分,引用松散:

MVC三元组类用于在Smalltalk-80中构建用户界面。模型是应用程序对象,视图是其屏幕显示,控制器定义了UI对用户输入的反应方式。MVC通过在视图和模型之间建立订阅/通知协议来分离它们。下图显示了一个模型和三个视图。为了简单起见,我们省略了控制器。

MVC都是关于UI的。重点在于模型和视图-定义和显示数据。请注意“订阅/通知协议”-这是您的控制器所在的位置。您可以构建所需的所有视图。只要他们遵守协议,您就无需接触模型或控制器。

如果您是专门谈论Web开发,恕我直言,许多流行的Web框架都带有术语MVC及其组件定义,因此既快速又宽松。


我是C#/ Java(那里只有几个项目)开发人员。看来我误解了模型的作用。将“业务逻辑”放入控制器中实际上只是一个后效(我的思路很好,我有数据模型(阅读:数据库连接/存储),所以我的业务逻辑需要进入控制器,因为我必须先应用它,然后再将数据存储到数据库中。只需将控制器上的所有内容下移一层即可。实际上,这解决了我目前遇到的问题(在一个程序中支持MySQL和MSSQL)
Steffen Winkler

0

您为什么不引入服务层?

然后,您的控制器将变得精简且更具可读性,然后您所有的控制器功能将变成纯动作。

您可以根据需要在服务层中分解业务逻辑。代码的可重用性更好,并且对模型和存储库没有影响。

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.