在哪里构建新的域实体?控制器、存储库或映射器


Where to build new domain entities? Controller, repository, or mapper?

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

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

是否:

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

    控制器创建UserEntity=>Repo=>Mapper=>DB

  2. 将表单数据转换为数组,并将其传递给UserRepository,然后UserRepository运行新的UserEntity()和setters,并将它传递给映射程序进行插入?

    控制器传递用户数据=>Repo创建UserEntity=>Mapper=>DB

  3. 将数组传递给UserRepository,由谁将数组传递到映射器以进行新的UserEntity和插入?

    控制器传递用户数据=>Repo传递用户数据>>映射器创建用户实体=>DB

谁的责任是管理对象的创建?

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

控制器

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

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

额外服务层

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

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

结论

采用这种设计:

Controller -> FormHandler -> ModelManager -> Mapper

我发现我的所有类现在都是可单元测试的(甚至在某种程度上是控制器),因为很好地分离了关注点,而单点交互对于避免重复逻辑是一个好处。

票据

在我看来,repo只是用来查询数据库——询问它是否有什么,而不是创建新的东西。

我在这个案例中的经验来自于使用Symfony 2和Doctrine 2。

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

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

其次,我认为控制器知道传入的数据以及对象是完全可以接受的。我同意"胖模型,瘦控制器",但说控制器不知道实体,这让控制器变得太瘦了。