假设对于每个域实体,我都有一个存储库,为数据映射器提供一个API。例如,如果我有一个UserEntity,那么我将有一个与UserMapper对话的UserRepository,以将用户数据持久化到数据库中。
现在,假设一个表单在网页上提交,我的控制器知道它需要根据提交的信息创建一个新的UserEntity。
是否:
-
当场执行new UserEntity(),并根据提交的表单数据运行所有必要的setter方法,然后将UserEntity传递给repo,repo传递给mapper进行插入?
控制器创建UserEntity=>Repo=>Mapper=>DB
-
将表单数据转换为数组,并将其传递给UserRepository,然后UserRepository运行新的UserEntity()和setters,并将它传递给映射程序进行插入?
控制器传递用户数据=>Repo创建UserEntity=>Mapper=>DB
-
将数组传递给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之间的差异而言,这可能不是很多内存或执行时间,但这是一个差异。我尽量快速失败。
其次,我认为控制器知道传入的数据以及对象是完全可以接受的。我同意"胖模型,瘦控制器",但说控制器不知道实体,这让控制器变得太瘦了。