Domain driven design 应用程序服务参数/返回类型

Domain driven design 应用程序服务参数/返回类型,domain-driven-design,Domain Driven Design,我从事标准web应用程序的工作,其领域是围绕DDD概念组织的。我想知道我的应用程序服务应该接受和返回什么样的对象。假设我有一个针对Useraggregate的应用程序服务 1)DTO/简单类型(字符串、int等) 公共接口用户应用程序服务{ 无效注册表用户(UserDTO UserDTO); 列出getUsersForOrganization(字符串organizationId); } 在这种情况下,应用程序服务负责调用汇编程序,将DTO转换为域对象,反之亦然 这种方法的优点是,我的应用程序服

我从事标准web应用程序的工作,其领域是围绕DDD概念组织的。我想知道我的应用程序服务应该接受和返回什么样的对象。假设我有一个针对
User
aggregate的应用程序服务

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

公共接口用户应用程序服务{
无效注册表用户(UserDTO UserDTO);
列出getUsersForOrganization(字符串organizationId);
}
在这种情况下,应用程序服务负责调用汇编程序,将DTO转换为域对象,反之亦然

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

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

2)域类型

public interface UserApplicationService {
  void registerUser(User user);
  List<User> getUsersForOrganization(OrganizationId organizationId);
}
公共接口用户应用程序服务{
无效注册表用户(用户);
列出getUsersForOrganization(OrganizationId OrganizationId);
}
在这种情况下,控制器/演示者负责转换

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

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

3)域对象+外观

这是在中使用的第三个选项。应用程序服务返回域类型,但有一些facade负责转换。因此,在我的例子中,控制器/演示者使用DTO与facade对话,facade进行转换,并使用域对象与应用程序服务对话。然而,在我看来,它似乎有点势不可挡——太多的层,太多的样板代码。对于一个应用程序服务来说,它可能听起来很棒,但是如果我们有几十个,那么我们需要有相同数量的facade方法——纯复制。此外,交易边界在哪里?

+1

我喜欢混合溶液

1) 我使用域对象作为参数,但仅限于ValueObjects。我认为应该谨慎地管理实体的生命周期,大多数时候视图没有足够的价值来填充整个实体,除了非常简单的CRUD应用程序。我看到很多次,一些开发人员粗心地通过构造函数初始化实体,只使用特定函数所需的部分字段填充它们,这使得为NullPointerException引入bug变得非常容易,而负责修复该问题的可怜人必须搜索数十种方法,以找到给定的实体的创建位置实体存储库中检索,或由我的项目中的工厂创建

有时为了简单起见,我使用一些表单对象作为参数

2) 我使用mvc控制器将应用程序服务返回的域对象转换为ViewAdapter(组件网将域模型与ui解耦),有时需要在这里完成dettach工作

3) 只有当应用程序服务需要通过远程过程调用(如web服务)公开时,我才使用FacadeDtos在本例中用作参数和返回类型,Facade负责转换Dto域模型

4) 如果应用程序服务需要同时向web视图和远程过程调用公开,那么验证就很麻烦。这会导致在表单对象和DTO上实现重复验证。我只验证简单的约束(不为null,仅举几个例子,长度由域对象以编程方式验证业务规则),因为我还没有找到一个完美的解决方案

希望这有帮助,如果有更好的解决方案,请让我知道

更新1:

1) 我必须承认,我不是这方面的大师,我也在努力寻找一个好的解决方案。因此,有时在我当前的解决方案中存在一些不一致性,比如您在评论中提到的表单bean。有时我们使用一些表单bean作为命令,并在其中放置一些域逻辑,所以在这种情况下,这些命令属于域层


2) 事务边界位于应用程序服务上。从技术上讲,域对象可能会在不经意间被修改出边界。我们通过团队纪律和代码审查来讨论这一点。

我通常倾向于使用命令和查询实现方法一

这是我这个周末要发表的一篇博文的一个片段

命令通过显式地支持无处不在的语言 在系统边界捕获用户意图-考虑使用 案例。它们作为域上的一层,将内部解耦 从外部,允许
public interface UserApplicationService {
  void registerUser(User user);
  List<User> getUsersForOrganization(OrganizationId organizationId);
}