REST API - DTO 与否?[已关闭]

2022-08-31 06:15:32

我目前正在为一个项目创建一个REST-API,并且一直在阅读有关最佳实践的文章。许多人似乎反对DTO,只是简单地公开了领域模型,而另一些人似乎认为DTO(或用户模型或任何你想称之为它的东西)是不好的做法。就个人而言,我认为这篇文章很有意义。

但是,我也了解DTO的缺点,其中包含所有额外的映射代码,域模型可能与DTO对应物100%相同等等。

我们的API主要是为了让其他客户端可以使用数据而创建的,但是如果我们做得对,如果可能的话,我们也希望将其用于我们自己的Web GUI。

问题是,我们可能不希望向其他客户端用户公开所有域数据。大部分数据只有在我们自己的Web应用程序中才有意义。此外,我们可能不希望在所有场景中公开有关对象的所有数据,尤其是与其他对象的关系等。例如,如果我们公开一个特定对象的列表,我们不一定想公开整个对象层次结构;这样物体的子体就不会暴露出来,而是可以通过链接(hateoas)被发现。

我应该如何解决这个问题?我正在考虑在我们的领域模型上使用 Jackson mixins 来控制在不同情况下会公开哪些数据。或者我们是否应该一直使用DTO - 即使考虑到它的缺点和争议?


答案 1

为什么应该在 REST API 中使用 DTO

DTO 代表 Data Transfer Object

创建此模式具有非常明确的目的:将数据传输到远程接口,就像 Web 服务一样。这种模式非常适合 REST API,从长远来看,DTO 将为您提供更大的灵活性

表示应用程序的模型和表示 API 处理的数据的模型是(或至少应该是)不同的关注点,应该彼此分离。您不希望在应用程序域模型中添加、删除或重命名字段时中断 API 客户端。

当服务层在域/持久性模型上运行时,API 控制器应基于一组不同的模型运行。例如,随着域/持久性模型的发展以支持新的业务需求,您可能希望创建 API 模型的新版本来支持这些更改。您可能还希望在新版本发布时弃用旧版本的 API。当事情解耦时,这是完全有可能实现的。


仅举一下公开 DTO 而不是持久性模型的一些好处:

  • 持久性模型与 API 模型分离。

  • DTO 可以根据您的需求进行定制,并且在仅公开持久性实体的一组属性时非常有用。您不需要@XmlTransient@JsonIgnore等批注来避免某些属性的序列化。

  • 通过使用 DTO,您将避免持久性实体中的大量批注,也就是说,您的持久性实体不会因与持久性无关的批注而膨胀。

  • 您可以完全控制在创建或更新资源时收到的属性。

  • 如果您使用的是 Swagger,则可以使用@ApiModel@ApiModelProperty注释来记录 API 模型,而不会弄乱持久性实体。

  • 对于每个版本的 API,可以有不同的 DTO。

  • 在映射关系时,您将拥有更大的灵活性。

  • 对于不同的媒体类型,可以有不同的 DTO。

  • 您的 DTO 可以包含 HATEOAS 的链接列表。这是不应该添加到持久性对象中的那种东西。使用Spring HATEOAS时,您可以使DTO类扩展PresentationModel(以前称为ResourceSupport)或使用EntityModel(以前称为Resource<T>)包装它们。

处理样板代码

您无需将持久性实体映射到 DTO,反之亦然。您可以使用许多映射框架来执行此操作。例如,看看MapStruct,它是基于注释的,可以用作Maven注释处理器。它在CDI和基于Spring的应用程序中都运行良好。

您可能还需要考虑龙目岛为您生成 getter、setter 和方法。equals()hashcode()toString()


相关:若要为 DTO 类提供更好的名称,请参阅此答案


答案 2

当您的 API 是公共的并且必须支持多个版本时,您必须使用 DTO。

另一方面,如果它是私有API,并且您同时控制客户端和服务器,我倾向于跳过DTO并直接公开域模型。