【问题标题】:MVC and the Repository Pattern: Roles of Controllers, Models, and Repositories?MVC 和存储库模式:控制器、模型和存储库的角色?
【发布时间】:2012-09-09 23:57:38
【问题描述】:

所以我一直在研究存储库模式的作用,它是一种将持久层与 MVC 框架中的模型解耦的方法。在此之前,我可能让我的UserModel 直接调用活动记录方法以存储/检索域对象。

这是我对调用堆栈的想法的草图,该请求应该创建一个新的User

这是我的问题:

  1. 这是存储库模式的正确实现吗?
  2. 我了解控制器应该从请求中获取用户信息并将其传递给模型。这通常是怎么发生的?控制器是否应该创建一个用户对象,然后将其传递给模型?我确实不想只将一组值传递给模型——我也不想将 15 个参数传递给创建用户的模型方法。
  3. 为了让这种模式真正起作用,在我看来,我需要一个域对象,它只是一个没有任何行为的简单数据结构,然后如果我使用 ORM,我将拥有一个 ORM对象,它将描述对象是如何被持久化的。最初我拒绝这样做,因为它感觉像是重复的代码,但如果我真的将持久性与业务逻辑分开,这将是必要的,对吧?例如,如果我使用内存存储怎么办?我将不再使用 ORM 对象。

我在这里的想法正确吗?这是可以接受的吗。请帮我把脑子里的点连起来。

【问题讨论】:

    标签: php model-view-controller design-patterns repository-pattern


    【解决方案1】:

    1。这是存储库模式的正确实现吗?

    我不确定你在哪里做这项研究,但你弄错了。

    • Repositories 用于将domain objectsdata mappers 分开。

    • 没有“模型”之类的东西。 MVC 设计模式中的模型是主要层之一:表示层模型层

    • 并且repository模式与active record(反)模式不兼容,将域和存储逻辑结合在一个实例中,从而导致SRP严重违规。

    要使用真实世界的示例,何时以及如何使用存储库,这里是一个示例:

    您正在创建一些文档管理工具,其中所述文档可以来自多个来源(例如:本地 SQL 数据库、SOAP 服务和缓存)。在这种情况下,您将创建一个存储库,它处理存储的“路由”。它是应用程序的一部分,它决定使用哪个数据映射器来存储/检索每个文档。

    存储库的目标是将域逻辑与与存储的交互分离。对于上面描述的系统,存储库还可以添加新的数据源,而无需重写大量代码(如果有的话)。您可以为文档添加另一种类型的映射器。

    2。控制器是否应该创建一个 User 对象,然后将其传递给模型?

    首先,控制器本身不应该创建任何东西。相反,您的控制器应该使用工厂来获取您需要的对象的实例。这个工厂可以通过构造函数或其他方法提供给控制器。这称为:依赖注入(要了解更多信息,watch this lecture)。

    此外,如上所述,模型是一个层,而不是任何特定的类或对象。控制器的职责是改变模型层的状态(通过传递数据给它)。您可以直接在控制器中与域对象和映射器(或存储库)交互,但这意味着泄漏控制器中的一些业务逻辑。建议改用services,然后操作所述域对象和存储相关结构。

    关于 10+ 参数的问题,您需要创建新用户帐户,假设您有以下足迹:

    public function postUser( Request $request )
    {
        ....
    }
    

    如果使用特定的Request 实例调用操作,您有两种选择如何处理大量参数:

    1. 将实例包装在decorator 中,这样您就可以调用一个方法来从特定数组中的请求中形成数据。然后将此数组传递给服务。

    2. 在控制器的动作中形成数组并将其传递给需要数据的地方。

    前一种解决方案更适合大规模应用程序,在这种应用程序中,需要在整个代码中反复形成这种数据。但在中小型项目中,第二种选择是常识性方法。

    事实上,控制器的工作是获取用户的输入,并将其分发到模型层和当前视图。而这种阵列的形成正好符合这一要求。

    3。 (..) 主要对象,它只是一个没有行为的简单数据结构,然后是 (..)

    没有。域对象不是“简单数据”。它是大多数领域业务逻辑驻留在应用程序中的地方。

    忘记神奇的 ORM。实现存储库的第一步是分离域和存储逻辑。域对象处理验证和业务规则,映射器处理持久性和数据完整性(小例子here)。

    您必须意识到的另一件事是,Web 应用程序的存储库并不真正与内存中的持久性交互(除了缓存)。相反,您的存储库会为不同的数据源处理映射器。

    【讨论】:

      【解决方案2】:

      控制器是否应该创建一个用户对象,然后将其传递给模型?

      我不确定您所说的“将其传递给模型”是什么意思——User 对象模型。 “控制器”和“模型”代表设计中的不同层,它们不是特定的对象,不应该像你提到的那样单独的UserModel对象。

      存储库接口本身通常被认为是模型的一部分,尽管域对象不应该保存自己——这应该在控制器中完成。

      您的控制器的工作将是解释请求并创建一个User 对象,然后使用存储库来保存用户:

      $user = new User(...); // based on Request
      $repository->save($user);
      

      在我看来,我需要一个域对象,它只是一个没有行为的简单数据结构

      这不是真的,您可以而且应该将行为封装在您的域对象中。至于持久化实际上是如何实现的,一个好的 ORM 应该会处理大部分细节,而您不必手动创建额外的类。

      【讨论】:

      • 当你说“一个好的 ORM 应该处理大部分细节,你不应该手动创建额外的类”,你的意思是这样的:$orm->User->CreateNew()?在我见过的大多数 ORM 中,当您插入一个新对象时,您首先创建该对象的一个​​实例,然后将其添加到一个集合中。
      • @MystereMan:我的意思是你不应该需要一个单独的“ORM 对象”。您应该能够使用 ORM 直接持久化 User 对象。
      • 根据域对象的复杂性,您应该需要一个单独的 ORM 对象。域对象!= ORM 对象。存储库处理域内容并处理域对象到 ORM 实体的“覆盖”,反之亦然。在简单的情况下,您可以直接使用 ORM 实体。在更复杂的情况下,最好将它们分开,因为这样很容易破坏具有持久性问题的域对象。无论您是否使用 ORM,存储库都会处理细节。
      • @MikeSW:确实,您应该让域对象忽略持久性问题。我的意思是你不应该为 ORM 对象手动创建一个单独的类,因为大多数 ORM 框架会自动为你做这个映射(例如通过 XML 配置)。
      猜你喜欢
      • 1970-01-01
      • 2011-11-26
      • 2020-10-18
      • 1970-01-01
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 2016-04-26
      相关资源
      最近更新 更多