我使用围绕 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 与外观对话,外观进行转换并使用域对象与应用程序服务对话。然而,以我的拙见,这似乎有点压倒性 - 太多的层,太多的样板代码。对于一个应用程序服务来说这听起来不错,但如果我们有几十个,我们需要有相同数量的外观方法——纯复制。此外,事务边界在哪里?