【问题标题】:Where to build new domain entities? Controller, repository, or mapper?在哪里构建新的域实体?控制器、存储库或映射器?
【发布时间】:2012-03-15 21:23:43
【问题描述】:

假设对于每个域实体,我都有一个为数据映射器提供 API 的存储库。例如,如果我有一个 UserEntity,那么我将有一个 UserRepository,它与 UserMapper 对话以将用户数据保存在数据库中。

现在,假设在网页上提交了一个表单,我的控制器知道它需要根据提交的信息创建一个新的 UserEntity。

是吗:

  1. 当场做new UserEntity(),根据提交的表单数据运行所有必要的setter方法,然后把UserEntity传给repo,谁传给mapper插入?

    Controller 创建 UserEntity => Repo => Mapper => DB

  2. 将表单数据转换为数组,并将其传递给 UserRepository,然后由后者运行 new UserEntity() 和 setter,并将其传递给映射器进行插入?

    Controller 传递用户数据 => Repo 创建 UserEntity => Mapper => DB

  3. 将数组传递给UserRepository,谁将数组传递给mapper以进行新的UserEntity和插入?

    Controller 传递用户数据 => Repo 传递用户数据 => Mapper 创建 UserEntity => DB

谁负责管理对象的创建?

【问题讨论】:

    标签: php instantiation separation-of-concerns


    【解决方案1】:

    我知道这个问题很老,但我想我会在这里提出我的想法,因为唯一的答案尚未被正式接受,这可能对未来的搜索者有所帮助。要直接回答您的问题,我会说以上都不是。我更喜欢有一个额外的服务,如果你愿意的话,一个“经理”,用于代理控制器和 repo/mapper 对象之间的交互。每个模型都有一个专门的管理器来处理它的创建、更新和删除。

    控制器

    我认为控制器是应用程序的粘合剂。我们可以将所有我们想要的关注点分成尽可能多的部分,但是在某个地方,必须同时理解视图端和模型端,而那个对象就是控制器。话虽如此,我认为控制器应该很瘦,所以控制器唯一真正的工作就是将请求映射到响应。任何类型的中间处理都应该在其他地方开始。

    在 CRUD 应用程序中,实例化新对象并将它们持久保存在控制器中非常容易,即使它不止一次完成,因为它只需粘贴几行代码。如果对象创建不是微不足道的怎么办?我正在维护一个具有许多复杂关系的应用程序,并且用户提交的创建通常需要同时创建许多对象。这在仅控制器和模型的环境中是不可行的。

    额外服务层

    为了处理这个问题,我创建了 2 个额外的服务层:FormHandler 和 Manager。每次提交表单时,表单内容都会发送到表单处理层。表单处理程序负责理解传入的表单数据并对其进行规范化。然后表单处理程序可以将数据传递给适当的管理器对象进行处理。管理器对象处理数据并更新领域层。他们负责创建模型、更改模型并将它们持久化到后端。

    通过这种方式,控制器了解请求、响应、表单(可能,如果您的框架支持服务器端表单创建)和 FormHandler。表单处理程序具有表单(或表单数据)和管理器的知识。 Manager 具有 Repository、Mapper 和 Model 的知识。请注意,现在,Manager 是与 Models 和 Mapper 交互的唯一点,他们不知道 Form 数据或 Request 或 Response。另一方面,控制器和表单处理程序不需要了解域层数据或持久性。

    结论

    采用这种设计:

    Controller -> FormHandler -> ModelManager -> Mapper

    我发现我的所有类现在都是可单元测试的(在某种程度上甚至是控制器),因为关注点分离被很好地分离出来,并且单点交互是避免重复逻辑的福音。

    备注

    我心目中的 repo 只用于查询数据库——询问它是否有东西,而不是创建新东西。

    我在这种情况下的经验来自于使用 Symfony 2 和 Doctrine 2。

    YMMV;例如你可能会发现表单层是不必要的,但我发现它非常方便将数据从表单/视图数据转换为领域模型可以理解的东西。

    【讨论】:

      【解决方案2】:

      这是一个很难回答的问题。出于几个原因,我的两美分排名第一。首先,假设您在实体中进行了域验证,这很可能会拒绝传递的数据。如果这发生在 2 或 3 中,那么在拒绝之前你已经深入了一些对象。就 2/3 和 1 之间的差异而言,它可能不是很多内存或执行时间,但它是一个差异。我试图快速失败。

      其次,我认为控制器知道传入的数据以及对象是完全可以接受的。我同意“胖模型,瘦控制器”,但说控制器不知道实体会使控制器太瘦到我喜欢的程度。

      【讨论】:

      • 对形势的分析很好。我很想听听其他想法,但我当然认为在 Controller 中创建它是可以接受的。但是,也许灵活性较差?例如,如果我在数据库中的 User 表中添加一个新字段,那么我必须记住转到创建 UserEntities 的每个 Controller。
      • 您很可能正在编辑接受所述新信息的视图(假设我们谈论的是传统的 Web 应用程序),编辑控制器并不太难。如果您忘记编辑控制器,您可能会忘记编辑视图。此外,一个简单的全局搜索可以找到你的类被使用的地方。
      • 另一个好点,如果控制器是答案,那么我并不难过......但是,我仍然有一部分感觉“内疚”在几个地方编写相同的实例化过程。不?同样,我设置了我的视图,以便添加反映新表更改的表单字段只需要在单个文件中进行更改。
      • 我同意你的观点,但是我相信如果你在多个不同的地方对一个特定实体执行相同的操作,你就会遇到更大的问题。如果您的应用程序结构正确,那么操作可能会发生根本性的不同。
      • 如果您在控制器中构建实体,那么听起来您不需要单独的存储库类。
      猜你喜欢
      • 2023-04-01
      • 1970-01-01
      • 2011-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-19
      • 2016-03-04
      • 1970-01-01
      相关资源
      最近更新 更多