【问题标题】:MVC with DAO/VO - Which DAO should the Controller talk to?带有 DAO/VO 的 MVC - 控制器应该与哪个 DAO 对话?
【发布时间】:2012-03-15 10:51:28
【问题描述】:

背景:

我有一个设计模式问题,希望有人能够解决。我用 PHP 编程,但我相信 DAO/VO 在 Java 中很流行。

我已经使用 MVC 很多年了。我设计了一个 MVC 但使用过程编程的购物。因此,最近我决定使用 OO 再次开发购物车。

问题:

我面临的问题是我的 Product 类没有 RetrieveAll() 方法。 例如。如果我列出了 10 种产品,我会从哪个实例调用 RetrieveAll() 方法?我有 10 种选择。

解决方案:

因此,我找到了 DAO/VO 模式。 除非我没有对这个模式进行足够的研究——我相信每个 DB 表都必须有一个 Model + DAO。任何模型或 DAO 都不应该知道另一组模型或 DAO。从而被封装。 该模式非常有意义,将数据库层从模型中拉开。

但是。在购物车中,我的产品被分配了类别。 类别可以是电子产品、服装等。

有 3 个表: - 类别(pid,名称) - 类别项目(iid,名称) - 类别链接(pid,iid)

从 MVC 方法来看,控制器应该与哪个 DAO 对话没有意义?

应该是:

  • 控制器与所有 3 个 DAO 对话,然后将适当的数据结构返回给视图?
  • 或者 DAO 是否应该相互交谈(以某种方式)并将单个结构返回给控制器?

Please see here for example (image)

【问题讨论】:

    标签: java php design-patterns


    【解决方案1】:

    我不确定你所说的 VO 是什么意思。是价值对象吗?

    我是DDD(领域驱动设计)方法的忠实拥护者(尽管我不认为自己是其中的大师)。在 DDD 中,您有所谓的 Services。 服务 是在您的域上运行并返回数据的操作。服务用您封装了域数据的操作。

    与其让控制器执行所有域逻辑,如检索什么项目、使用什么 DAO 等等(为什么控制器应该关心域?),它应该封装在它自己的域中,在 DDD服务中的案例。

    例如,您要检索“电子”类别的所有类别项目。 你可以编写一个看起来像这样的控制器(如果代码有无效的语法,请原谅我,这是为了举例):

    public function showItemsByCategoryAction($categoryName) {
      $categoryId = $categoryDAO->findByName($categoryName);
      if(is_null($categoryId)) {
        //@TODO error
      }
    
      $itemIds = $categoryLinkDAO->getItemsByCategoryId($categoryId);
      if(empty($itemIds)) {
        //@TODO show error to the user
      }
    
      $items = $categoryItemDAO->findManyItems($itemIds);
    
      //@TODO parse, assign to view etc
    }
    

    这至少引入了两个问题:

    1. 控制器是FSUC(胖傻丑控制器)
    2. 代码不可重复使用。如果您想添加另一个表示层(例如开发人员的 API、网站的移动版本等),您将不得不复制粘贴相同的代码(期望视图渲染的部分),最终您会来到封装此代码的东西,这就是服务的用途。

    使用服务层,相同的控制器可能看起来像

    public function showItemsByCategoryAction($categoryName) {
      $service = new Item_CategoryName_Finder_Service();
      $items = $service->find($categoryName);
    
      if(empty($items)){
        //@TODO show empty page result, redirect or whatever
      }
    
      $this->getView()->bind('items', $items);
    }
    

    控制器现在干净、小巧,所有域逻辑都封装在一个服务中,可以在代码中的任何地方重用。

    现在有些人认为controller应该对DAOs一无所知,只能通过Services与Domain进行通信,其他人说可以从controllers调用DAOs,没有严格的规则,决定什么更适合你。

    希望对您有所帮助! 祝你好运:)

    【讨论】:

    • 我认为域服务包含了不适合域对象的域操作。我认为域服务根本不应该返回数据,它们可能会返回数据,但这不是它们的目的。这是存储库处理数据库访问的责任
    • @MikeSW 是的,你可能是对的。但问题并不是关于 DDD,我只是将 DDD 中的设计模式(服务)带入了问题的上下文。如果您想使用正确的 DDD,那么存储库不应该对数据库一无所知,并且应该使用 DAO 来访问存储。我还在某处听说最好不要将存储库、DOA 等暴露给控制器,而是只暴露服务层,因此控制器使用不同的服务来创建/编辑/操作/查找域中的数据,以及域中的服务turn 使用存储库、工厂等。
    • 我有点不同意这里。只有存储库应该以任何形式了解数据库。关于服务,我认为那些是应用服务,但在这种情况下,它们只是一个补充层,我的意思是我不认为它们在这里增加任何价值,因此控制器也扮演应用服务的角色。跨度>
    • 我不是在争论存储库它不是主题 ATM。但是,如果您想说,通过 API 或 Web 浏览器添加对您网站的另一个访问权限呢?您不能重用相同的控制器(因为它也具有与不同类型的访问不同的表示逻辑)所以我认为最终您会将这些对存储库的调用拉到其他级别,随意调用它我只是调用它服务。
    • 控制器怎么会有表示逻辑?!你是对的,将应用程序服务功能与控制器相结合会使改变事情变得有点困难,但是,此时(如果要求不暗示其他几种用户界面)你只有类只会增加数量没有任何价值的代码。说实话,在 WebApi 的情况下,我可以毫无问题地重用控制器,因为控制器与输入和输出数据格式分离(我使用的是 asp.net mvc,所以这可能只是一个框架细节)跨度>
    【解决方案2】:

    我也不是 DDD 方面的专家,但这是我的看法。这是应用存储库模式的情况。基本上,Domain 不知道也不关心 DAO 或其他任何与 rpesistence 相关的东西。最多知道存储库接口(应该在基础设施级别实现)。

    控制器知道域和存储库。存储库封装了与数据库相关的所有内容,应用程序只知道存储库本身(实际上应该注入作为实际实现的接口)。然后在存储库中,你有你认为合适的 DAO。存储库仅接收和发送回应用程序/域对象,与数据库访问实现无关。

    简而言之,任何与 db 相关的东西都是一部分,它是存储库的实现细节。

    【讨论】:

    • 您只需在 DAO 上创建另一个级别(存储库),但问题仍然存在。我应该使用什么?一个调用其他存储库的特定存储库?还是按顺序调用每个存储库(请参阅我的答案中带有 DAO 的示例)?按命名类别查找所有项目是任何实体和存储库都无法提供的操作。为了封装和可重用性,它应该被封装成类似 Service 的东西。而且不一定是DDD上下文中定义的Service,也可以叫Action或者Command,只是封装了一些常用操作的层。
    • 存储库的目的很明确:将持久性细节与应用程序的其余部分分开。 DAO 是实现细节。存储库接口是根据应用使用情况设计的,可以说存储库本身就是应用程序使用的一种服务。按命名类别查找所有项目正是存储库的一种方法,它将使用必要的 DAO 来获得这些结果。然后存储库将持久性对象“转换”为应用程序对象(通常通过自动映射)
    • 我同意您所描述的存储库的角色。但是让我们澄清一下:存储库可以调用另一个存储库吗?哪个存储库应该实现方法FindItemsByCategoryName、CategoryRepository 或ItemsRepository?
    • 如果你想要一个具体的答案, QueryItemsRepository 。再一次,存储库是为应用程序使用而设计的,而不是围绕 DAO。我知道许多开发人员根据实体概念或类似的概念使用存储库,但我发现它是人为的而且几乎没有用。
    • 感谢 MikeSW 的回答。我相信您所说的是在 DAO 上方创建另一个层。但是我的 DAO 只保存数据库查询。他们唯一的作用是访问数据库并用相关数据填充 VO(值对象)或通过我的 DB 层执行 CRUD。然后它将 VO/s 返回给控制器。我应该提到的一件事是我已经将文件“打包”到可以移植到其他定制系统的模块中。例如。在另一个电子购物车中使用我的类别模块。因此,我不希望基础设施中的任何底层相互通信。再次感谢
    【解决方案3】:

    在决定哪个 dao 方法应该转到哪个 dao 类时可以考虑返回类型,因此控制器应该与哪个 dao 对话:

    为每个数据实体实现一个 DAO 类更简洁,

    CRUD 操作应该进入 Dao 类, C-创建、R-读取、U-更新、D-删除

    读取操作不像创建、更新、删除,大多数时候读取操作在考虑它们返回的内容时有不同的风格。

    对于Read操作,在决定哪个dao方法应该去哪个dao类时可以考虑返回类型

    以下是一些商业实体和道

    Exchange -> ExchangeDao
    Company -> CompanyDao
    Stock -> StockDao
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-29
      • 2013-07-22
      • 1970-01-01
      • 2021-03-19
      • 2014-10-16
      • 1970-01-01
      • 2016-02-15
      • 2018-01-11
      相关资源
      最近更新 更多