【问题标题】:ASP.NET MVC - Should business logic exist in controllers?ASP.NET MVC - 控制器中应该存在业务逻辑吗?
【发布时间】:2010-09-19 02:41:36
【问题描述】:

Derik Whitaker 几天前发布了一个 article,这引起了我一段时间以来的好奇:控制器中应该存在业务逻辑吗?

到目前为止,我看到的所有 ASP.NET MVC 演示都将存储库访问和业务逻辑放在控制器中。有些甚至还会在那里进行验证。这导致了相当大、臃肿的控制器。这真的是使用MVC框架的方式吗?看起来这最终会导致大量重复的代码和逻辑分布在不同的控制器上。

【问题讨论】:

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


【解决方案1】:

我喜欢Microsoft Patterns & Practices 提供的图表。而且我相信这句格言“一张图胜过千言万语”。

【讨论】:

  • 这真的很有用!你能告诉我你是在那个网站上找到这张图表的吗?
  • 这是来自微软的“服务器端实现”msdn.microsoft.com/en-us/library/hh404093.aspx
  • 好的,但是在 MVC 应用程序中 - 业务逻辑在哪里?似乎我们需要一个额外的服务层之类的?!
【解决方案2】:

如果您使用依赖注入器,您的业务逻辑将转到它们,因此您将获得整洁干净的控制器。

【讨论】:

    【解决方案3】:

    业务逻辑不应包含在控制器中。控制器应该尽可能瘦,最好遵循模式:

    1. 查找域实体
    2. 作用于域实体
    3. 为查看/返回结果准备数据

    另外,控制器可以包含一些应用程序逻辑。

    那么我应该把我的业务逻辑放在哪里呢?在模型中。

    什么是模型?这是一个很好的问题。请参阅Microsoft Patterns and Practices article(感谢 AlejandroR 的出色发现)。这里有三类模型:

    • 视图模型:这只是一个数据包,其中包含在视图之间传递数据的最少逻辑(如果有的话),包含基本的字段验证。
    • 域模型:具有业务逻辑的胖模型,对单个或多个数据实体进行操作(即实体 A 处于给定状态,而不是实体 B 上的操作)
    • 数据模型:存储感知模型,包含在单个实体中的逻辑仅与该实体相关(即如果字段 a 则字段 b)

    当然,MVC 是一种不同类型的范例。我这里描述的是 MVC 只占用顶层,视频this article on Wikipedia

    今天,MVC 和类似的模型-视图-展示器 (MVP) 是关注点分离设计模式,专门应用于更大系统的表示层。在简单的场景中,MVC 可能代表系统的主要设计,直接进入数据库;然而,在大多数情况下,MVC 中的控制器和模型对服务或数据层/层都有松散的依赖关系。这是关于客户端-服务器架构的全部内容

    【讨论】:

      【解决方案4】:

      您可以查看显示 Validating with a Service Layer 的 Stephen Walther 的这个很棒的教程。

      了解如何移动验证 控制器动作的逻辑 并进入一个单独的服务层。在 本教程,斯蒂芬·瓦尔特 解释了如何保持锋利 通过隔离来分离关注点 您的服务层来自您的 控制器层。

      【讨论】:

      • 这是最正确的答案。我个人进一步主张不要将服务暴露给控制器,而是选择使用诸如 MVVM 模式中的 ViewModel 概念。想象一个场景,您想编写一个带有桌面界面(例如,Windows 窗体或 WPF)和 Web 界面的业务应用程序。解决该问题会导致您使用此处所提倡的“瘦控制器”模式。底线:永远不要将业务逻辑放入模型或控制器中,也不要将任何你没有的东西放入控制器中。
      【解决方案5】:

      这是一个有趣的问题。

      我认为有趣的是,大量示例 MVC 应用程序实际上未能遵循 MVC 范式,即真正将“业务逻辑”完全置于模型中。 Martin Fowler 指出,MVC 不是四人组意义上的模式。相反,如果程序员要创建超出玩具应用程序的东西,则必须将模式添加到 是一种范式。

      所以,简短的回答是“业务逻辑”确实不应该存在于控制器中,因为控制器具有处理视图和用户交互的附加功能,而我们希望创建只有一个目的的对象。

      一个更长的答案是,在将逻辑从控制器移动到模型之前,您需要对模型层的设计进行一些思考。也许您可以使用 REST 处理所有应用程序逻辑,在这种情况下,模型的设计应该相当清晰。如果没有,你应该知道你将使用什么方法来防止你的模型变得臃肿。

      【讨论】:

        【解决方案6】:

        业务逻辑确实应该在模型中。你应该瞄准胖模型,瘦控制器。

        例如,而不是:

        public interface IOrderService{
            int CalculateTotal(Order order);
        }
        

        我宁愿:

        public class Order{
            int CalculateTotal(ITaxService service){...}        
        }
        

        这假定税收是由外部服务计算的,并且要求您的模型了解与外部服务的接口。

        这将使您的控制器看起来像:

        public class OrdersController{
            public OrdersController(ITaxService taxService, IOrdersRepository ordersRepository){...}
        
            public void Show(int id){
                ViewData["OrderTotal"] = ordersRepository.LoadOrder(id).CalculateTotal(taxService);
            }
        }
        

        或者类似的东西。

        【讨论】:

        • 那么您会将服务注入您的控制器而不是存储库吗?在这种情况下,工作单元原则如何发挥作用?
        • 我又写了一些东西,我希望这更有意义。您可能还想阅读:weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model 尽管它是关于 Rails 的,但它仍然非常适用。
        • 我个人将存储库称为服务。
        • 它们绝对是一种服务,但专门用于数据访问。这只是我使用的约定,不是我特别提倡的。
        • 这将使您的模型与 ItaxService 紧密耦合。如果您想在另一个项目或其他 dll 中重用模型,则必须有 ItaxService 实现或引用,否则您的模型将被破坏,从而导致违反 SOLID 原则。 ItaxService 应该有您的模型的参考。通过这种方式,您可以在其他项目中重用您的模型,而无需 ItaxService 参考。
        猜你喜欢
        • 1970-01-01
        • 2011-09-03
        • 2011-04-20
        • 2017-08-08
        • 1970-01-01
        • 2021-02-24
        • 1970-01-01
        • 2013-03-14
        • 2016-04-22
        相关资源
        最近更新 更多