API REST - DTOs ou não? [fechadas]

154

Atualmente, estou criando uma API REST para um projeto e tenho lido artigos e artigos sobre melhores práticas. Muitos parecem ser contra os DTOs e simplesmente expõem o modelo de domínio, enquanto outros parecem pensar que os DTOs (ou Modelos de Usuário ou o que você quiser chamar) são uma má prática. Pessoalmente, pensei que este artigo fazia muito sentido.

No entanto, também entendo as desvantagens dos DTOs com todo o código de mapeamento extra, modelos de domínio que podem ser 100% idênticos aos de seus equivalentes e assim por diante.

Nossa API é criada principalmente para que outros clientes possam consumir dados; no entanto, se fizermos o certo, também gostaríamos de usá-lo para nossa própria GUI da Web, se possível.

A questão é que talvez não desejemos expor todos os dados do domínio para os outros usuários clientes. Muitos dos dados só farão sentido em nosso próprio aplicativo da web. Além disso, talvez não desejemos expor todos os dados sobre um objeto em todos os cenários, especialmente relacionamentos com outros objetos e assim por diante. Por exemplo, se expusermos uma lista de um objeto em particular, não desejaríamos necessariamente expor toda a hierarquia do objeto; para que os filhos do objeto não sejam expostos, mas possam ser descobertos através de links (hateoas).

Como devo resolver esse problema? Eu estava pensando em usar mixins Jackson em nossos modelos de domínio para controlar quais dados seriam expostos em diferentes cenários. Ou devemos apenas usar DTOs o tempo todo - mesmo considerando suas desvantagens e controvérsias?

benbjo
fonte
9
Não se surpreenda se esta pergunta for encerrada. É mais uma pergunta baseada em discussão, o que significa que não há uma resposta correta e clara. Pergunte a pessoas diferentes e você obterá uma resposta diferente.
Ben Thurley 23/03
2
O link do artigo ( ibm.com/developerworks/community/blogs/barcia/entry/… ) está quebrado.
pinkpanther
7
@pinkpanther Esse é um ótimo artigo e é uma pena que não esteja mais disponível. Aqui está a versão em cache do Web Archive .
Cassiomolin

Respostas:

251

Por que você deve usar DTOs em sua API REST

DTO significa D ata T ransferência O bject .

Esse padrão foi criado com uma finalidade muito bem definida: transferir dados para interfaces remotas , assim como serviços da web . Esse padrão se encaixa muito bem em uma API REST e os DTOs fornecerão mais flexibilidade a longo prazo.

Os modelos que representam o domínio do seu aplicativo e os modelos que representam os dados manipulados por sua API são (ou pelo menos devem ser) preocupações diferentes e devem ser dissociados . Você não deseja interromper seus clientes de API ao adicionar, remover ou renomear um campo do modelo de domínio do aplicativo.

Enquanto sua camada de serviço opera sobre os modelos de domínio / persistência, seus controladores de API devem operar sobre um conjunto diferente de modelos. À medida que seus modelos de domínio / persistência evoluem para oferecer suporte a novos requisitos de negócios, por exemplo, convém criar novas versões dos modelos de API para suportar essas alterações. Você também pode descontinuar as versões antigas da sua API à medida que novas versões são lançadas. E é perfeitamente possível conseguir quando as coisas são dissociadas.


Apenas para mencionar alguns benefícios da exposição de DTOs em vez de modelos de persistência:

  • Separar modelos de persistência de modelos de API.

  • Os DTOs podem ser adaptados às suas necessidades e são ótimos ao expor apenas um conjunto de atributos de suas entidades de persistência. Você não precisará de anotações como @XmlTransiente @JsonIgnorepara evitar a serialização de alguns atributos.

  • Ao usar DTOs, você evitará muitas anotações em suas entidades de persistência, ou seja, suas entidades de persistência não serão inchadas com anotações não relacionadas à persistência.

  • Você terá controle total sobre os atributos que está recebendo ao criar ou atualizar um recurso.

  • Se você estiver usando o Swagger , poderá usar @ApiModele @ApiModelPropertyanotações para documentar seus modelos de API sem prejudicar suas entidades de persistência.

  • Você pode ter DTOs diferentes para cada versão da sua API.

  • Você terá mais flexibilidade ao mapear relacionamentos.

  • Você pode ter DTOs diferentes para diferentes tipos de mídia.

  • Seus DTOs podem ter uma lista de links para HATEOAS . Esse é o tipo de coisa que não deve ser adicionada aos objetos de persistência. Ao usar o Spring HATEOAS , você pode estender suas classes DTO RepresentationModel(anteriormente conhecidas como ResourceSupport) ou envolvê-las com EntityModel(anteriormente conhecidas como Resource<T>).

Lidando com o código padrão

Você não precisará mapear suas entidades de persistência para DTOs e vice-versa manualmente . Existem muitas estruturas de mapeamento que você pode usar para fazer isso. Por exemplo, dê uma olhada no MapStruct , que é baseado em anotação e funciona como um Processador de anotação Maven. Funciona bem em aplicativos baseados em CDI e Spring.

Você também pode querer considerar Lombok para gerar getters, setters, equals(), hashcode()e toString()métodos para você.


Relacionado: Para dar um nome melhor às suas aulas de DTO, consulte esta resposta .

cassiomolin
fonte
2
Se eu seguisse o caminho do DTO, você mapearia todos os objetos de domínio para um DTO ou apenas aqueles que não seriam idênticos? Além disso, como você resolveria o problema de expor dados com base em diferentes cenários / contextos? Vários DTOs por objeto de domínio?
21716 benbjo
6
@benbjo Depende de você. Normalmente, mapeio apenas as entidades mais complexas para os DTOs, as entidades que não quero que tenham todos os atributos expostos e as entidades com muitos relacionamentos. Os DTOs me dão a flexibilidade de ter uma lista de links a serem usados ​​no HATEOAS. Esse é o tipo de coisa que eu não adicionaria aos meus objetos de persistência.
cassiomolin
2
@molin muito obrigado pelas informações e sugestões. Definitivamente vou dar uma olhada no MapStruct. À primeira vista, parece atender muito bem às minhas necessidades.
benbjo
6
Caro downvoter, você poderia ao menos explicar o motivo do seu voto negativo?
cassiomolin
8
Há também um motivo arquitetônico para usar DTOs em vez de entidades de domínio na API REST. A API REST não deve ser alterada para evitar a quebra de clientes existentes. Se você usar o modelo de domínio diretamente na API, criará um acoplamento indesejável entre a API e o modelo de domínio. De acordo com o princípio de design do acoplamento flexível de serviço, o contrato de serviço não deve ser totalmente acoplado à lógica de serviço ou aos detalhes da implementação.
Paulo Merson
25

Quando sua API é pública e você precisa oferecer suporte a várias versões, é necessário usar DTOs.

Por outro lado, se é uma API privada e você controla o cliente e o servidor, tendem a pular os DTOs e expor diretamente o modelo de domínio.

David Siro
fonte
Concordo com você na última parte e geralmente o faço, mas esta é minha primeira API pública. Vou considerar o que você diz sobre o uso de DTOs para a parte pública. Talvez as partes pública e privada da API devam ser separadas, mesmo que "coma seu próprio alimento para cães" seja um bom princípio.
21716 benbjo
11

Eu costumo usar DTOs.

Não gosto das desvantagens, mas parece que as outras opções são ainda piores:

A exposição de objetos do domínio pode levar a problemas de segurança e vazamento de dados. As anotações de Jackson podem parecer resolver o problema, mas é muito fácil cometer um erro e expor dados que não devem ser expostos. Ao projetar uma classe de DTO, é muito mais difícil cometer esse erro.

Por outro lado, as desvantagens da abordagem de DTO podem ser reduzidas com coisas como mapeamento de objeto a objeto e Lombok por menos clichê.

Argb32
fonte
9

Como você já se declarou, essa é claramente uma questão relacionada à opinião. Eu mesmo sou mais atraído pela abordagem Sem DTOs, simplesmente por causa de todo o código padrão necessário.

Isto é principalmente verdade para o lado da resposta de uma API json / rest. Até escrevi um addon jackson para evitar escrever muitas visualizações / filtros json para esses casos: https://github.com/Antibrumm/jackson-antpathfilter

Por outro lado, os DTOs são bons no lado da entrada de solicitação dessas APIs. Trabalhar diretamente em entidades pode ser bastante difícil, levando em consideração as relações bidirecionais, por exemplo. Além disso, você realmente não deseja permitir que um chamador modifique um atributo "criador", por exemplo. Portanto, você não deve permitir certos campos durante o mapeamento de tais solicitações.

Martin Frey
fonte
2
Concordo que minha pergunta esteja um pouco relacionada à opinião (e desanimada), mas também estava procurando dicas sobre como resolver meu problema. Vou levar muito no seu addon Jackson, no entanto, você acha que usar mixins para controlar quais dados devem ser expostos em diferentes cenários é um bom caminho a seguir?
benbjo