DDD-在使用映射器类时不会暴露吸气剂

时间:2018-09-17 17:41:09

标签: java domain-driven-design

我最近有很多关于DDD的文章。我有一些基础知识(并在实践中使用过),但现在我决定(几乎)采用100%DDD。当然,我马上就遇到了问题。

我为每个模块(功能)分为3层:应用程序,域和基础结构。我使用六角形架构模式,这基本上意味着我在域类中获得了核心逻辑,应用层使用了它(但是域层根本不了解应用程序层),基础架构从域(数据库存储库)和一些接口实现了我的端口来自应用层等。

现在,当我在应用程序服务中处理一些用例时,我必须使用我的根聚合并执行一些逻辑,最后将其映射到UI的某些DTO。问题是要执行这样的映射,我必须为我的大多数属性提供吸气剂/设定剂,这会使我丧命。我想通过提供许多业务方法并且仅使用很少的getter / setter来避免贫血的模型。

我可以看到2种解决方案:

  • 在我的域层中引入DTO,这又是DDD,并且在我的实体中具有toDTO()方法
  • 提供获取器/设置器,仅在映射器中使用

任何其他解决方案,您如何处理应用程序中的此类常见问题?我知道实际上很难完全采用DDD,并且不遵循所有规则也是可以的,但是我注意到,尽可能少的吸气剂/吸气剂对我的设计有很大帮助,但同时很明显DTO不属于完全域化。

3 个答案:

答案 0 :(得分:2)

  

问题在于,要执行这样的映射,我必须为我的大多数属性提供吸气剂/设定剂,这会使我丧命。

是的-我为此战斗了很长时间。真正的答案是没有魔术。

如果您希望聚合有用,则需要能够以某种方式从中获取信息。只写数据库不是很有趣。如果界面上没有可用的query,那么首先将信息放入事物中就没有多大意义了。

从域中获取的域特定查询是可以接受的。关键限制

  1. 应该不可能通过操纵返回值来更改聚合的状态。因此,我们倾向于返回没有变量的对象或这些对象的副本。

  2. 当消费者查询我们,对查询结果执行一些操作然后根据这些操作的结果选择命令时,我们不应鼓励使用这种惯用法。

    //不要这样做: int x = o.X(); x = x + 1; o.Y(x)

您可以做一些事情,以使代码总体上看起来更“干净”。

1)让聚合响应值对象的查询,然后查询这些值对象以获取构建DTO所需的信息。

2)将工厂方法传递到集合中以获取所需的数据

<T> T query(API<T> api)

API<T>是聚合可以与之交互的构建者/工厂。

3)由聚合实现两个单独的接口(一个用于查询,一个用于命令),并且仅授予调用者对其所需接口的访问权限。

4)有一个查询点可以从聚合中获取“当前状态”,然后从中构建其他所有内容。

答案 1 :(得分:2)

沃恩·弗农(Vaughn Vernon)在红皮书的第14章(应用程序)中进行了处理,在“用户界面”部分(第512页)中,他提出了一些替代方案:

  • dtos
  • 调解人
  • 域有效负载对象
  • 国家陈述
  • 用例最佳存储库查询(与cqrs相似)
  • 数据转换器

希望有帮助。

答案 2 :(得分:0)

应用CQRS可以避免在AR中使用吸气剂。另外,您甚至可以将AR中的属性数量减少到仅需要满足一些不变性的属性。

让我解释得更好。

CQRS(命令查询职责隔离)完全区分了应用程序提供的命令和查询操作。

Command操作通过执行一些用例来更改应用程序的状态。执行命令的结果是一个域事件,其中包含更改的信息。

然后,将域事件(建模为DTO)用于构建您的视图模型,该视图模型包含您需要从查询返回的所有数据。视图模型是一个DTO,易于序列化和通过电线发送。

因此,您的查询操作(用于用信息填充UI的那些操作)甚至不会充斥您的AR。它们适用于您的视图模型,因此对它的任何更改都不会对您的AR产生任何影响。

最后,正如我在开头所提到的,大多数时候,我们在AR中拥有大量的属性,只是为了将这些信息保存在数据库中。但是,此信息永远不会用于满足任何不变性。例如,当我们保存用户名但在任何其他用例中都不再使用该数据时。如果该信息已经保留在domain事件中并已投影到您的视图模型中,则可以对其进行查询,这样就可以避免在AR中保留该字段,以使其尽可能保持干净。