Pular para o conteúdo principal

Data Transfer Object

Descrição​

O Data Transfer Object (DTO) é um objeto que carrega dados entre processos para reduzir o número de chamadas. Como cada chamada remota é cara, o objetivo é fazer menos chamadas, cada uma levando mais dados. Passar muitos parâmetros é desajeitado, e algumas linguagens retornam apenas um valor, então o DTO agrupa tudo o que uma chamada precisa enviar ou receber.

Um DTO é um objeto simples, só com campos e acessores, sem comportamento de domínio, e precisa ser serializável para cruzar a fronteira. Normalmente um Assembler (ou Mapper) converte entre o DTO e os objetos de domínio, de modo que o domínio não conhece o formato de transporte.

Um benefício secundário é encapsular a serialização: o formato de transmissão (JSON, XML, binário) fica em um único lugar, e o restante do código não depende dele. Historicamente o padrão foi chamado de "value object" em Java, o que conflita com o padrão Value Object; por isso o nome "Transfer Object" se firmou.

Problemas que resolve​

  • Muitas chamadas remotas pequenas para obter ou enviar um conjunto de dados relacionados.
  • Assinaturas com longas listas de parâmetros ou necessidade de retornar vários valores.
  • Objetos de domínio acoplados ao formato de serialização e ao contrato remoto.
  • Exposição acidental de campos internos, relacionamentos preguiçosos e regras do domínio para clientes externos.

Quando usar​

  • Em interfaces remotas, geralmente por trás de um Remote Facade, onde cada chamada tem custo alto.
  • Quando se quer agrupar o que seriam várias chamadas em uma só.
  • Quando o contrato de dados com o cliente deve evoluir independentemente do modelo de domínio.

Quando evitar​

  • Em chamadas locais dentro do mesmo processo: Fowler alerta no artigo LocalDTO que o custo de mapeamento é alto e o benefício é nenhum; prefira uma Service Layer trabalhando com objetos de domínio.
  • Quando o DTO tende a espelhar o domínio campo a campo, apenas duplicando classes sem ganho de isolamento.
  • Quando o DTO ganha comportamento e regras de negócio, o que o transforma em um modelo anêmico paralelo.

Trade-offs​

GanhoCusto / Risco
Menos chamadas remotas, mais dados por chamadaMais classes e código de mapeamento para manter
Contrato remoto desacoplado do domínioDuplicação de campos entre DTO e entidade
Serialização concentrada em um pontoRisco de transferir dados demais ou de dados desatualizados
Controle do que sai do sistemaMapeamento custa tempo e pode esconder erros de conversão
  • Desempenho: compensa só quando a rede é o gargalo; localmente, é custo puro.
  • Acoplamento: o domínio fica livre de anotações e detalhes de serialização.
  • Testabilidade: DTOs são triviais, mas o Assembler merece testes próprios.
  • Evolução: versionar o DTO permite mudar o domínio sem quebrar clientes.

Diagrama​

O Client recebe um OrderDTO da RemoteFacade. O OrderAssembler é o único que conhece tanto o DTO quanto o Order do domínio.

Exemplo​

Uma API devolve um pedido com cliente e itens em uma única resposta. O DTO é um registro imutável e o assembler converte a entidade.

public record OrderLineDTO(String sku, int quantity, long priceCents) {}

public record OrderDTO(String id, String customerName, List<OrderLineDTO> lines) {}

public class OrderAssembler {
public OrderDTO toDTO(Order order) {
List<OrderLineDTO> lines = order.getLines().stream()
.map(l -> new OrderLineDTO(
l.getProduct().getSku(), l.getQuantity(), l.getPrice().cents()))
.toList();
return new OrderDTO(order.getId(), order.getCustomer().getName(), lines);
}
}

Em frameworks, o papel do assembler é feito por bibliotecas como MapStruct (Java), AutoMapper (.NET) e serializers do Django REST Framework.

Padrões relacionados​

  • Remote Facade — o consumidor típico do DTO: a fachada recebe e devolve DTOs.
  • Service Layer — a alternativa local, que dispensa DTOs e trabalha com o domínio.
  • Mapper — o Assembler é um mapper entre DTO e domínio.
  • Value Object — nome historicamente confundido; o Value Object tem comportamento e igualdade por valor.
  • Data Mapper — faz algo parecido entre domínio e banco de dados; o assembler faz entre domínio e fronteira remota.
  • Remote Procedure Invocation — o estilo de comunicação em que o custo de cada chamada justifica o DTO.

Referência​