胖模型/瘦控制器与服务层[关闭]


83

我使用.Net开发企业应用程序已有很多年了。我的应用程序通常具有一个域模型,其中包含映射到SQL DB表的实体。我使用存储库模式,依赖注入和服务层。

最近,我们开始从事MVC 3项目的工作,我们在争论应该在哪里放置哪种逻辑。我遇到了瘦控制器/ FAT模型架构,并且想知道服务层如何适合

选项1-服务示范

控制器很薄,在模型上调用方法。这些模型“知道”如何从数据库加载自身并与存储库或服务对话。例如,customerModel具有Load(id)方法,并加载客户和一些子对象,例如GetContracts()。

选项2-控制器与服务对话

控制器要求服务检索模型对象。加载/存储等逻辑在服务层中。该模型是仅包含数据的纯实体模型。

为什么选项1会是一个更好的选择,尤其是当我们谈论企业应用时,我的经验告诉我要分开关注点,将模型和控制器保持尽可能的薄,并让专门的服务执行业务逻辑(包括数据库交互)

感谢您的所有建议和对良好资源的引用。

Answers:


94

所有这些取决于您的应用程序的意图和要求。

就是说,这是我对“中等规模”(不是本地餐厅,而不是Twitter / Facebook)Web应用程序的建议。

  1. 精益领域建模

    干燥的POCO样式对象,最好不了解Web应用程序的MVC架构,以使其与您的特定实现保持尽可能松散的耦合。甚至可以重新包装类库以供外部应用程序使用,例如通过WCF Web服务的REST API。 )。

    MVC中的“模型”最准确地表示控制器知道的模型,因此是供View使用的模型

    在较小的(通常是教程)应用程序中,“应用程序/域模型层”的实体模型通常与控制器交付给视图的实例化对象相同。

    在大型应用程序中,开发人员经常采用MVVM体系结构的原则,并开始使用单独的View Model对象。控制器经常调用与下面看不见的实体一起工作的中间层服务。在这种情况下,MVC中的M最准确地表示视图模型。

  2. 强大的服务层

    这并不意味着肥胖的逻辑,而是精心编写的单一目的服务。虽然在模型之外的服务中对业务逻辑进行编码要比纯粹的“ OOP”更具“过程性”,但它对松散的耦合,测试和灵活的部署(例如n层部署)有很大帮助。

    在我的个人实践中,我既在数据层向下编写服务代码,又将我对POCO对象的行为建模(持久性机制,低级别验证等)和更高级别的服务(业务/工作流程功能)紧密联系在一起MVC机制。

  3. 精益控制器

    我确保我的控制者仅仅是教练,因为它既不是比赛(服务)也不是球员(实体模型或视图模型),而是仅决定谁扮演哪个位置和做出什么角色。我的控制器执行以下两项操作:

    1. 与实体/域模型交互的呼叫服务

    2. 为适当的视图准备一个视图模型。

    甚至经过身份验证/授权的控制器操作也可以通过注入的服务/属性来完成。


编辑1:

请记住,这并不意味着您的实体/域模型是或必须贫乏。欢迎使用ORM,存储库和工厂,验证或状态机制。它仅仅意味着对于中等规模的应用程序,该型号在MVC代表的意思为控制器模式,移交给你的视图

希望这一点能够使福勒使徒们平静下来,他们相信贫民窟的数据模型是一种反模式。同时,它反映比OOP一个稍微程序角,其中它是更纯的,以包括在建模类的行为。

没有“终极真理”,但是使用这种模式,您会发现构建,测试和部署应用程序很容易-同时保持大量可重用性和可伸缩性。


编辑2:

就是说,即使对于中等大小的应用程序而言,过度设计(通常是一个书呆子组成的?)系统也是太普遍了。例如,用存储库模式包装ORM,然后编写服务以使用存储库...所有这些都有利于分离关注点等,但是如果您的项目不需要(并且不太可能很快需要) ),请勿构建它。完全跳过存储库,针对ORM编写瘦业务服务(例如查询类),甚至让您的控制器直接与其对话都没有错。这一切都取决于规模。


编辑3:

我想指出的是,这种解释和建议是针对服务器端MVC架构(如ASP.Net)的上下文,而不是针对客户端端框架(如Knockout或Backbone)的。


11
除了控制器不了解存储库之外,这几乎与我使用的设计模式完全相同。控制器仅与服务交互,而服务又与存储库交互。
Lester 2012年

2
@莱斯特我编辑来清除它。95%的时间都不这样做,其想法是服务起作用。在小型应用程序上,它可能会过大,但对任何人来说都是一个好习惯,并且使用IoC容器更容易维护
one.beat.consumer 2012年

1
+1 @ one.beat.consumer:这与我在项目中所采用的方法相同...有时对规则过于单纯会导致解决方案过于复杂,您可以体验到从现实世界证明的解决方案中获得的更多好处,不能完全遵循GOF模式
themarcuz 2012年

7
MVC中的@ivowiblo模型是控制器准备并传递给View的任何数据模型。这就是为什么您的“应用程序模型”(域模型,模型层,无论您使用什么标签)完全不知道MVC库的原因,甚至不存在于解决方案之外的单独分布式系统中。在MVC中,请求只是路由到控制器。控制器组装一个视图模型(表示层的数据)。如果这种模式是你在你的持久性机制,不良做法可能使用相同的实例化的对象,但它允许的,这意味着没有专属的定义。
one.beat.consumer 2012年

2
对于+1,请牢记MVC中的“模型”最准确地表示控制器知道的模型,因此也就是用于视图的模型。
Luiz

16

在我们继续讨论如何放置所有内容之前,您需要了解有关MVC的更多信息。好吧,如果您想遵循这种模式。否则,您可以立即停止阅读。

模式定义非常宽松。没有任何内容说明控制器,视图或模型的外观或结构。该模式仅说明您应分开各部分以及它们之间如何相互作用。因此,让我们进一步了解它们是什么(我的解释)。

MVC

模型 模型可以是任何东西。它可以是Web服务,您的存储库,您的服务类,也可以只是您的域模型。模型是用于获取所需信息的所有内容。将“模型”视为一个层,而不是单个对象。

控制器 控制器是胶水。它从模型中获取信息,并使其适应视图,反之亦然。

视图 视图仅应呈现用户看到的内容。

请注意,您不应将模型与视图模型混淆。Microsoft确实应该将“模型”文件夹命名为“ ViewModels”,因为这就是它们的名称。我不会在视图中直接使用“模型”中的信息。如果不这样做,则意味着如果更改了视图,则必须更改模型,反之亦然。

答案

该模型不是视图模型,而是一个层。模型中的所有内容均用于获取视图所需的信息。控制器获取该信息并将其放入单个视图模型中。

单个控制器动作可能使用一个或多个对“模型”的调用,以便能够组合视图所需的信息。

这意味着,如果要获得易于维护和扩展的应用程序,则第二个选项是最正确的。

请注意,可能不需要服务层。您可以直接从控制器调用OR / M。但是,如果您发现自己在复制代码或使用繁琐的控制器,则只需将逻辑移至服务层即可。因为您使用的是正确的视图模型,所以控制器不会受到该更改的影响。


3
我希望ASP.NET MVC可以改名为ASP.NET ModelView视图控制器。这将是一个可怕的名字,但至少它将传达其真实含义:)
Hector Correa 2012年

甚至在使用ASP.NET MVC意识到模型并不意味着视图模型之后,我花了一段时间。
Lester 2012年

@ one.beat.consumer:我对模型的观点是它可以是任何东西。它只是那里的一层。创建它适合于应用程序。我之所以这样说,是因为很多人认为ASP.NET MVC中的模型是视图模型,或者VM和模型是相同的。
jgauffin 2012年

我确实认为我解决了这个问题。我对customerModel他在问题中的解释是一种视图模型。如果他知道并非如此,那么答案就更加明显了。
jgauffin 2012年

2
@jgauffin语义在这里很重要-在MVC中,“模型”并不表示“模型层”;它表示适合Controller传递给View的模型对象。在相当大的应用程序中,MVC架构通常甚至不知道模型/数据层或您选择调用的任何层。我编辑后的答案试图解释这种困惑……主要是在应用程序较小时,通常不需要额外分离Model和View模型,因此人们倾向于标记其模型并让控制器使用存储库等。全尺寸的应用程序,这种情况很少发生。
one.beat.consumer 2012年

0

选项1:您可能认为模型==服务。模型也是业务层。

选项2是Anemic Domain Model反模式。 http://en.wikipedia.org/wiki/Anemic_domain_model


请记住,调用某种反模式需要更多上下文!许多应用程序不需要域模型,因为它们所做的大多数事情都是CRUD操作。
Rookian

域模型只是带有“元数据”的数据,如果您没有元数据,那就可以了。我删除了“反模式”一词,因为您是正确的。我真的很喜欢这个被接受的答案,而我自己应该对此发表评论。
Imre L

0

选项2是所谓的Fat Stupid Ugly Controllers架构(此表达式的引用)。该解决方案通常打破了MVC的精神,因此违反了MVC精神。


1
public ActionResult FetchApple() { return View(_groceryService.GetApple("Granny Smith")); }如果你问我,我就很瘦。
one.beat.consumer 2012年

4
我对FSUC文章的阅读与上面的选项2不符。FSUC作者提供的示例未显示封装了所有排序逻辑的服务层的使用。相反,它表明控制器已经加载了业务逻辑。现在,由于处于控制器中,业务逻辑的可重用性已丧失。
Marvo 2012年
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.