15

我使用围绕 DDD 概念组织的域的标准 Web 应用程序。我想知道我的应用程序服务应该接受并返回什么样的对象。假设我有一个User聚合应用程序服务。

1) DTO / 简单类型(字符串、整数等)

public interface UserApplicationService {
  void registerUser(UserDTO userDTO);
  List<UserDTO> getUsersForOrganization(String organizationId);
}

在这种情况下,应用程序服务负责调用汇编程序将 DTO 转换为域对象,反之亦然。

这种方法的优点是我的应用程序服务是我的域对象的清晰边界。另一个是应用服务是一个明确的事务边界。由持久性上下文管理的域对象不会泄漏到事务之外的某个地方。

缺点是在表单的情况下,验证必须基于DTO。因此,我的验证规则在域(对象负责其状态)和 DTO 验证规则之间重复。(与Spring MVC 示例应用程序一样)。此外,如果视图的某些部分需要另一种形式的模型(假设 UserDTO 没有足够的信息来呈现视图),我将需要创建另一个 DTO 并基于从应用程序服务返回的几个 DTO,组合另一个,由视图使用。

2) 域类型

public interface UserApplicationService {
  void registerUser(User user);
  List<User> getUsersForOrganization(OrganizationId organizationId);
}

在这种情况下,控制器/演示者负责转换。

最大的缺点是我的域对象从应用程序服务中泄漏 - 没有明确的分离。另外,我们的交易边界在哪里?可能附加到的域对象(例如 Hibernate 会话)泄漏到应用程序服务层之外。(但是,我注意到这是编写了多少示例应用程序。)

优点可能是控制器/演示者负责为视图准备模型,因此它可以根据视图要求组成DTO。例如,视图可能需要一些在 DTO 中未从 #getUsersForOrganizationMethod 返回的附加信息。此外,验证可能基于域对象,因此它不会在 DTO 和域对象之间重复。

3)领域对象+门面

这是DDDsample 应用程序中使用的第三个选项。应用程序服务返回域类型,但有一些外观负责转换。所以在我的例子中,控制器/演示者使用 DTO 与外观对话,外观进行转换并使用域对象与应用程序服务对话。然而,以我的拙见,这似乎有点压倒性 - 太多的层,太多的样板代码。对于一个应用程序服务来说这听起来不错,但如果我们有几十个,我们需要有相同数量的外观方法——纯复制。此外,事务边界在哪里?

4

3 回答 3

7
于 2013-08-01T01:47:10.940 回答
4

我通常发现自己倾向于方法一,使用命令和查询。

这是我本周末发布的博客文章的片段。

命令通过在系统边界显式捕获用户意图来帮助您支持无处不在的语言——想想用例。它们充当您领域的一个层,将内部与外部解耦,允许您逐步引入内部概念,而不会破坏外部。命令执行器为您提供了一个很好的管道,您可以利用它来集中安全性、性能指标、日志记录、会话管理等。而且,如果那是你的事 - 命令可以异步处理。

你可以在我的博客上找到一个例子;http://www.jefclaes.be/2013/01/separating-command-data-from-logic-and.html

关于您对验证的担忧,请记住,重复验证并不可怕; http://gorodinski.com/blog/2012/05/19/validation-in-domain-driven-design-ddd/

于 2013-08-01T07:57:41.570 回答
0

我使用了您的所有三种方法,但以非常一致的方式使用了 Eric Evans 在 DDD蓝皮书中描述的分层原则。

应用层的输入始终是简单类型(或封装它们的对象参数),输出始终是域类型,表示层处理与视图类型 (DTO) 之间的映射。

我在这里回答一个非常相似的问题时解释了我采用这种方法的原因:https ://stackoverflow.com/a/41230822/509891

于 2016-12-22T18:26:06.367 回答