DDD - 在拥有映射器类时不暴露吸气剂

Posted

技术标签:

【中文标题】DDD - 在拥有映射器类时不暴露吸气剂【英文标题】:DDD - not exposing getters while having mapper class 【发布时间】:2018-09-17 17:41:08 【问题描述】:

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

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

现在,当我在应用程序服务中处理一些用例时,我必须使用我的根聚合并执行一些逻辑,最后将它映射到一些用于 UI 的 DTO。问题是,要执行这样的映射,我必须为我的大部分属性提供 getter/setter,这让我很生气。我想通过提供大量业务方法而只提供很少的 getter/setter 来避免贫血模型。

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

在我的领域层中引入 DTO,这又是 DDD,并在我的实体中具有 toDTO() 方法 提供 getter/setter 并仅在映射器中使用

任何其他解决方案,你们如何处理应用程序中的此类常见问题?我知道在现实中很难完全实现 DDD,不遵守所有规则也没关系,但我注意到尽可能少的 getter/setter 对我的设计有很大帮助,但同时很明显 DTO 不属于到域。

【问题讨论】:

我会避免两者,因为您可能有许多 DTO 横穿不同的模型。 DTO 可以携带来自用户的用户名和来自地址的街道和号码的集合。而另一个 DTO 可能有用户的名字和姓氏,以及访问网站的次数。 是的,但有什么替代方案? 我可以告诉你我在做什么,但不确定它是否是你正在寻找的答案。我将 DTO 放在表示层中。在服务层使用模型并在返回结果时创建并填充 DTO。为了从模型中获取数据,我使用 getter。并且 DTO 具有所有公共属性,没有方法。 你不需要 setter,只需要 getter 来阅读它 【参考方案1】:

问题是,为了执行这样的映射,我必须为我的大部分属性提供 getter/setter,这让我很头疼。

是的 - 我与它斗争了很长时间。真正的答案是没有魔法。

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

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

    不应通过操作返回值来更改聚合的状态。所以我们倾向于返回没有修改器的对象,或者这些对象的副本。

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

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

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

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

2) 将工厂方法传递到聚合中以获取您需要的数据

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

其中API&lt;T&gt; 是聚合可以与之交互的构建器/工厂。

3) 有两个由聚合实现的独立接口(一个用于查询,一个用于命令),并且只允许调用者访问他们需要的接口。

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

【讨论】:

【参考方案2】:

Vaughn Vernon 在红皮书的第 14 章(应用程序)中处理了这个问题,在“用户界面”部分(第 512 页)中,他公开了一些替代方案:

dtos 调解员 域负载对象 状态表示 用例优化存储库查询(对 cqrs 关闭) 数据转换器

希望对你有帮助。

【讨论】:

【参考方案3】:

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

让我解释得更好。

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

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

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

因此,您的查询操作(用于在您的 UI 中填充信息的操作)甚至不会为您的 AR 提供水分。它们适用于您的视图模型,因此对其进行的任何更改都不会对您的 AR 产生任何影响。

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

【讨论】:

以上是关于DDD - 在拥有映射器类时不暴露吸气剂的主要内容,如果未能解决你的问题,请参考以下文章

可以自动更新数据库模式的 O/R 映射器?

如果映射器在中途失败并且 Hadoop 重试该映射器,自定义计数器会发生啥

双层设备映射器 - 自定义 dm-crypt

根据映射器代码中的某些逻辑,将映射器中的一些数据(行)写入单独的目录

MyBatis 生成器 - 生成映射器实现

纯 Java MyBatis 映射器?