我目前正在为一个项目创建REST-API,并且一直在阅读关于最佳实践的文章。许多人似乎反对dto,只是简单地公开域模型,而其他人似乎认为dto(或用户模型或任何您想要称呼它的东西)是不好的实践。就我个人而言,我认为这篇文章很有意义。
但是,我也理解dto的缺点,包括所有额外的映射代码、可能与dto对应物100%相同的领域模型等等。
我们的API主要是为了让其他客户端可以使用数据而创建的,但是如果我们做得对,我们也想在可能的情况下将其用于我们自己的web GUI。
The thing is that we might not want to expose all the domain data to the other client users. Much of the data will only make sense in our own web application. Also, we might not want to expose all data about an object in all scenarios, especially relationships to other objects and so on. For example, if we expose a list of a particular object we would not necessarily want to expose the entire object hierarchy; so that the object's children will not be exposed, but can be discovered through links (hateoas).
我该如何着手解决这个问题呢?我在考虑在我们的领域模型上使用Jackson mixins来控制在不同的场景下会暴露哪些数据。或者我们应该一直使用dto——即使考虑到它的缺点和争议?
为什么应该在REST API中使用dto
DTO代表数据传输对象。
创建此模式的目的非常明确:将数据传输到远程接口,就像web服务一样。这种模式非常适合REST API,从长远来看dto将为您提供更大的灵活性。
表示应用程序领域的模型和表示API处理的数据的模型是(或者至少应该是)不同的关注点,并且应该彼此分离。当从应用程序域模型中添加、删除或重命名字段时,您不希望破坏API客户机。
当你的服务层在域/持久化模型上操作时,你的API控制器应该在一组不同的模型上操作。例如,当您的域/持久化模型发展到支持新的业务需求时,您可能希望创建API模型的新版本来支持这些更改。当新版本发布时,您还可能希望弃用旧版本的API。当它们解耦时,这是完全有可能实现的。
只是提一下公开dto而不是持久性模型的一些好处:
Decouple persistence models from API models.
DTOs can be tailored to your needs and they are great when exposing only a set of attributes of your persistence entities. You won't need annotations such as @XmlTransient and @JsonIgnore to avoid the serialization of some attributes.
By using DTOs, you will avoid a hell of annotations in your persistence entities, that is, your persistence entities won't be bloated with non persistence related annotations.
You will have full control over the attributes you are receiving when creating or updating a resource.
If you are using Swagger, you can use @ApiModel and @ApiModelProperty annotations to document your API models without messing your persistence entities.
You can have different DTOs for each version of your API.
You'll have more flexibility when mapping relationships.
You can have different DTOs for different media types.
Your DTOs can have a list of links for HATEOAS. That's the kind of thing that shouldn't be added to persistence objects. When using Spring HATEOAS, you can make your DTO classes extend RepresentationModel (formerly known as ResourceSupport) or wrap them with EntityModel (formerly known as Resource<T>).
处理样板代码
您不需要手动将持久性实体映射到dto,反之亦然。您可以使用许多映射框架来完成此任务。例如,看一看MapStruct,它是基于注释的,作为Maven注释处理器工作。它在CDI和基于spring的应用程序中都工作得很好。
您还可以考虑使用Lombok为您生成getter、setter、equals()、hashcode()和toString()方法。
相关:为了给DTO类起更好的名字,请参考这个答案。
为什么应该在REST API中使用dto
DTO代表数据传输对象。
创建此模式的目的非常明确:将数据传输到远程接口,就像web服务一样。这种模式非常适合REST API,从长远来看dto将为您提供更大的灵活性。
表示应用程序领域的模型和表示API处理的数据的模型是(或者至少应该是)不同的关注点,并且应该彼此分离。当从应用程序域模型中添加、删除或重命名字段时,您不希望破坏API客户机。
当你的服务层在域/持久化模型上操作时,你的API控制器应该在一组不同的模型上操作。例如,当您的域/持久化模型发展到支持新的业务需求时,您可能希望创建API模型的新版本来支持这些更改。当新版本发布时,您还可能希望弃用旧版本的API。当它们解耦时,这是完全有可能实现的。
只是提一下公开dto而不是持久性模型的一些好处:
Decouple persistence models from API models.
DTOs can be tailored to your needs and they are great when exposing only a set of attributes of your persistence entities. You won't need annotations such as @XmlTransient and @JsonIgnore to avoid the serialization of some attributes.
By using DTOs, you will avoid a hell of annotations in your persistence entities, that is, your persistence entities won't be bloated with non persistence related annotations.
You will have full control over the attributes you are receiving when creating or updating a resource.
If you are using Swagger, you can use @ApiModel and @ApiModelProperty annotations to document your API models without messing your persistence entities.
You can have different DTOs for each version of your API.
You'll have more flexibility when mapping relationships.
You can have different DTOs for different media types.
Your DTOs can have a list of links for HATEOAS. That's the kind of thing that shouldn't be added to persistence objects. When using Spring HATEOAS, you can make your DTO classes extend RepresentationModel (formerly known as ResourceSupport) or wrap them with EntityModel (formerly known as Resource<T>).
处理样板代码
您不需要手动将持久性实体映射到dto,反之亦然。您可以使用许多映射框架来完成此任务。例如,看一看MapStruct,它是基于注释的,作为Maven注释处理器工作。它在CDI和基于spring的应用程序中都工作得很好。
您还可以考虑使用Lombok为您生成getter、setter、equals()、hashcode()和toString()方法。
相关:为了给DTO类起更好的名字,请参考这个答案。