为什么数据传输对象(DTO)是反模式的?

我最近无意中听到有人说 数据传输对象(DTO)是 反模式

为什么? 还有什么选择?

72247 次浏览

OO 纯粹主义者会说,DTO 是反模式的,因为对象变成了数据表表示,而不是真正的域对象。

我认为人们的意思是,如果您将所有远程对象实现为 DTO,那么它可能是一个反模式。DTO 仅仅是一组属性,如果您有大对象,那么即使您不需要或不使用它们,您也总是会传输所有属性。在后一种情况下,更喜欢使用代理模式。

有些人认为 DTO 是一种反模式,因为它们可能会被滥用。

这篇文章含糊地描述了一些弊端。

“ EJB 3.0中的反模式 DTO”(原始链接目前离线)说:

Entity 的重量级特性 之前的 EJB 规范中的 bean EJB 3.0,导致使用 像数据传输这样的设计模式 对象(DTO) 轻量级对象(这些对象应该具有 是实体豆本身 (第一个地方) ,用于发送 跨层数据... 现在是 EJB 3.0 Spec 使实体 bean 模型相同 作为普通的旧 Java 对象(POJO) 这个新的 POJO 模式,你不会 更长的时间需要为每个 实体或一组实体... 如果 您希望发送 EJB 3.0实体 让他们公正 实现 java.io.Serialiazable

我并不认为 DTO 本身就是一种反模式,但是存在与 DTO 的使用相关的反模式。比尔•达德尼(Bill Dudney)以 DTO 爆炸为例:

Http://www.softwaresummit.com/2003/speakers/dudneyj2eeantipatterns.pdf

这里还提到一些滥用 DTO 的情况:

Http://anirudhvyas.com/root/2008/04/19/abuses-of-dto-pattern-in-java-world/

它们起源于三层系统(通常使用 EJB 作为技术) ,作为在层之间传递数据的一种手段。大多数现代的基于诸如 Spring 这样的框架的 Java 系统采用另一种简化的视图,将 POJO 作为域对象(通常使用 JPA 等进行注释)。.)在一个单一的层... 在这里使用 DTO 是没有必要的。

有些项目将所有数据放置两次 ,一次作为域对象,一次作为数据传输对象。

这个 复制的代价是巨大的,所以架构需要从这个分离中获得巨大的好处,这样才是值得的。

DTO 不是反模式。当您通过网络发送一些数据(比如,通过 Ajax 调用发送到网页)时,您需要确保只发送目的地将使用的数据来节省带宽。此外,表示层通常可以方便地使用与本机业务对象略有不同的数据格式。

我知道这是一个面向 Java 的问题,但是在。NET 语言的匿名类型、序列化和 LINQ 允许动态构造 DTO,这减少了使用它们的设置和开销。

如果您正在构建一个分布式系统,那么 DTO 当然不是一个反模式。不是每个人都会在这种意义上开发,但如果你有一个(例如)开放社交应用程序所有运行的 JavaScript。

它将向您的 API 发送大量数据。然后将其反序列化为某种形式的对象,通常是 DTO/Request 对象。然后可以对此进行验证,以确保输入的数据在转换为模型对象之前是正确的。

在我看来,它被视为反模式,因为它被误用了。如果您没有构建一个分布式系统,那么很可能就不需要它们。

数据传输对象的目的是存储来自不同来源的数据,然后立即将其传输到数据库(或 远程外观)中。

但是,DTO 模式违反了单一责任原则,因为 DTO 不仅存储数据,而且还将数据从数据库/facade 传输到数据库/facade。

需要将数据对象从业务对象中分离出来并不是反模式,因为 分离数据库层可能无论如何都需要这样做。

应该使用聚合和存储库模式来代替 DTO,该模式将对象集合(总数)和数据传输(仓库)分开。

要传输一组对象,您可以使用 工作单位模式,该模式包含一组 Repository 和一个事务上下文; 以便在事务中分别传输聚合中的每个对象。

当您让所有域对象急切地加载相关对象时,DTO 就成为一种必需而不是反模式。

如果你不做 DTO,你将会有不必要的对象从你的业务层转移到你的客户/网络层。

为了限制这种情况下的开销,可以转移 DTO。

问题不应该是“为什么”,而应该是“ 什么时候”。

它肯定是 反模式时,只有使用它的结果是较高的成本运行时或维护。我所从事的项目拥有数百个与数据库实体类相同的 DTO。每次你想添加一个字段的时候,你需要添加 id 四次-到 DTO,到实体,从 DTO 到域类或实体的转换,反向转换,... 你忘记了一些位置和数据变得不一致。

这是 当您真正需要域类的不同表示时,不需要反模式-更平,更丰富,..。

就个人而言,我从一个域类开始并传递它,在正确的地方进行适当的检查。我可以注释和/或添加一些“助手”类,使映射到数据库,序列化格式,如 JSON 或 XML... 我总是可以分裂一个类到两个,如果我觉得需要的话。

这是关于你的观点——我更喜欢把域对象看作扮演不同角色的单个对象,而不是从彼此创建的多个对象。如果对象的唯一角色是传输数据,那么它就是 DTO。