Sobre DTOs em java

Olá, tenho bastantes duvidas sobre DTOs e como organizar eles, o chatGpt fala algo, depois volta atras e fala outra, acredito quue uma discussão aqui ajudaria muito a aprender. minhas perguntas iniciais são:

  1. é algo "bem visto" criar varios DTOs de uma mesma classe para diferentes partes do sistema?
  2. onde eu coloco o "toDTO" de uma classe? eu colocaria num Service ou no construtor do DTO, qual é a mais seguida?
  3. para trazer de volta um UserDto em User, eu só passo os atributos do DTO pro user e deixo alguns campos como o da senha e email vazio, ou eu uso algum parametro do dto como referencia para buscar um User la no banco de dados?
  4. para referenciar classes de dominios (as que estão lá no banco de dados) eu coloco diretamente uma Instancia dentro da classe(numa classe Post colocar a classe User, que vem com senha e tudo) ou coloco um Dto dentro da classe de dominio para representar a User (com bancos de Dados NOSql, que é a agregados)

essas foram as perguntas mas eu gostaria de saber num geral mesmo sobre os DTOs, até que eu conseguir usá-los numa boa, agradeço a paciencia.

  1. Tudo depende do contexto, pode ser bom ou não. Lembre-se que isso é complexidade, é algo a mais para mexer em vários pontos quando altera algo. Então se não tem um gerador de código atuando, pode ser um enorme problema. Compensará ter essa complexidade?
  2. Até onde eu sei não tem um lugar clara e universalmente definido. Eu sequer sei se deveria ter um método assim, o construtor não é suficiente?
  3. Depende. Se todas as regras estivessem escritas não precisaria de programador para decidir. Tem que olhar os requisitos e ver isso.
  4. Não parece que nada disso é necessário, mas pode ser só que o texto esteja confuso.

DTO é algo muito simples, se começa complicar então ele não está sendo só DTO: https://pt.stackoverflow.com/q/31362/101

Faz sentido para você?

Espero ter ajudado.


Farei algo que muitos pedem para aprender a programar corretamente, gratuitamente. Para saber quando, me segue nas suas plataformas preferidas. Quase não as uso, não terá infindas notificações (links aqui).

Cara, eu não sei se é a melhor forma, mas eu faço o seguinte: Eu crio uma classe que será o DTO de algum objeto, e dentro dela, eu coloco todos os atributos. Após isso, eu coloco um construtor normal, com os atributos como parâmetro, e um outro construtor que recebe o objeto original como parâmetro. Esse seria o "toDTO". Depois eu crio um método chamado "to(nome do seu objeto)Object", e nesse método, ele retorna uma instância do objeto original utilizando as propriedades que estão no DTO. E o negócio de usar todos os atributos, é que não precisa criar 2 DTOs, um para requisição e outro de resposta, porque caso use ele como requisição, o usuário só vai instanciá-lo passando os parametros que você definiu no constructor; e como resposta, é só instanciá-lo passando um objeto original como parâmetro do constructor. Isso é bom porque talvez você queira que algumas propriedades sejam retornadas na resposta, porém não quer que elas sejam preenchidas na hora da criação, aí você define isso com os constructors. No constructor padrão, você decide quais propriedades serão iniciadas, e no constructor que recebe um objeto, você decide quais propriedades você quer exibir do objeto original.

Enfim, não sei se essa é a melhor prática, mas eu utilizo esse e funciona muito bem para o que eu preciso, e além de ficar clean, não precisa criar 2 DTOs diferentes.

Inicialmente, gosto de criar um DTO para cada Entity. Na camada de serviço, faço a busca da entidade no banco e já converto para um DTO, então nenhum Entity sai da service. Se houver necessidade de algum modelo mais específico, monto um novo DTO com dados personalizados, se possível tento não fazer classes DTOs muito grandes, pois inevitavelmente acaba gerando complexidade (já vi classes DTO com uns 200 atributos, é horrível de dar manutenção). Quanto a conversão para dto e para entity, dentro da classe DTO tenho além do construtor padrão, um construtor que recebe a entidade, e para o caminho inverso, dentro do próprio DTO crio um método toEntity() que retorna uma instância da entitidade.

Olá amigo. DTOs são classes para transferir dados entre camadas da sua aplicação. Você sempre verá as perguntas sendo respondidas com "Depende" e "Varia o contexto". E é verdade. Programar envolve saber o que usar, quando e onde. Vou tentar responder de acordo com a minha experiência de desenvolvimento.

  1. Sendo sincero, costumo usar DTOs geralmente na camada de borda (Controllers, Chamadas de APIs, Mensageria, etc). Como java trabalha com referência de memória, você pode passar o objeto completo entre as camadas de sua aplicação. Claro que isso não é regra, vou invocar o varia o contexto.
  2. Isso geralmente vai de acordo com a arquitetura do seu projeto e gostos pessoais... Gosto de trabalhar com uma arquitetura hexagonal, onde cada camada do sistema é separada. Minhas classes de domínio possuem apenas o básico POJO de uma entidade. Toda manipulação é feita em uma camada de serviço, então neste caso o meu toDTO ou fromDTO fica na camada de serviço. O pessoal mais centrado no encapsulamento forte costuma deixar esse método dentro da classe do próprio objeto, ou como método ou recebendo o DTO num construtor.
  3. O que é que você quer fazer? Neste caso, eu tenho dois DTOs (ou records), um específico para as credenciais e um outro específico para updates nos dados do usuário. Cada um utilizado no seu contexto. O fluxo que atualiza os dados do usuário não envolve a atualização da senha, e vice versa. DTOs podem (e geralmente devem) ser criados para o objetivo específico dos dados que ele irá transportar. Você pode talvez estudar se uma aplicação de Patch não seja mais viável para você, ao invés de um PUT com a entidade completa. Isso também depende do seu conhecimento HTTP.
  4. Não, DTO nada mais é que um carregador, transportador de informações. Classes de domínios não são (nem podem ser) fundamentalmente DTOs. Seus relacionamentos serão sempre entre uma entidade e outra. Sua preocupação em relação a senha deve ser a criptografia do conteúdo dela, para que ela não seja salva no banco. Ou, você pode usar uma estratégia de carregamento Lazy nos campos que você não quer recuperar a primeira instância, caso esteja usando um ORM e recuperando a entidade toda. Ou ainda, você pode usar uma projection do spring data (um exemplo) para recuperar apenas o que aquela rota precisa do banco e mapear para um DTO (ou record), nada mais, nada menos. Isso excluiria a senha já na camada de persistência, nem do banco ela viria. Para exemplo, você pode criar um DTO que disponibilize numa rota GET apenas os dados do cliente necessários para a visualização, o que não incluiria a senha.

DTOs (e records) são uma mão na roda. Um exemplo disso é que você pode querer uma rota de paginação ou uma rota para buscar todos os registros de uma entidade para uma listagem em uma tabela, mas a sua tabela não vai ter todos os dados de toda a entidade numa linha. Isso fica para a visualização completa do item que você está tratando. Você pode ter uma rota só pra listar o básico na tabela (id, nome, cpf) e o botão de editar vai chamar a rota com o id, e nela você visualiza todos os dados do cliente que não foram trazidos para a tabela. Com isso você otimiza sua consulta no banco de dados, tráfego de rede e processamento.

Há muito que se possa fazer com DTO (e records). Vê como citei records várias vezes? rsrs. É uma implementação mais limpa do que o DTO, mas para dados de entrada, ainda se limita a não permitir Bean Validation, teria que fazer a validação numa função dedicada. Espero ter contribuido com sua dúvida.