DEV Community

Cover image for [PT-BR] Padrão de Projeto: Data Transfer Object
Leonardo Santos
Leonardo Santos

Posted on • Updated on

[PT-BR] Padrão de Projeto: Data Transfer Object

Sempre quando ouvia falar sobre padrão de projeto eu já imaginava alguma implementação ou arquitetura muito complexa para o que eu estava acostumado. Porém, nos últimos tempos, me dei conta que estava trabalhando com um padrão de projeto que facilitava muito a "conversa" entre os sistemas clientes e os sistemas servidores e gostaria de explicá-lo nesse artigo.

Espero que a compreensão de algo que, para mim, num geral, era super complexo seja mais simples para vocês.

Data Transfer Object

O Data Transfer Object (DTO), ou conhecido em alguns lugares apenas como Transfer Object (TO), é um padrão em que utilizamos classes que possuem informações que são formadas a partir de uma ou mais entidades (classes cujos atributos são mapeados para os campos de uma tabela em um banco de dados). Essas classes tem o objetivo de simplificar a maneira como um sistema deve conversar com outro em relação à algum processo.

DTO na prática

O exemplo que demonstrei a seguir leva em consideração um processo de guardar um novo registro Usuário na base de dados e a resposta que a nossa API irá retornar.

Nesse exemplo, estarei utilizando uma implementação em Java com Spring Boot.

Classes

Nossa classe UsuarioEntity (responsável por fazer o mapeamento da nossa classe e atributos com a tabela e campos da base de dados):

package com.leonardossev.dtoexemplo.model.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "usuario")
public class UsuarioEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "nome")
    private String nome;

    @Column(name = "email", unique = true)
    private String email;

    @Column(name = "senha")
    private String senha;

    // Getters e Setters...

}

Enter fullscreen mode Exit fullscreen mode

Nosso Repository (interface responsável por interagir com a base de dados):

package com.leonardossev.dtoexemplo.repository;

import com.leonardossev.dtoexemplo.model.entity.UsuarioEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UsuarioRepository extends JpaRepository<UsuarioEntity, Long> {
}

Enter fullscreen mode Exit fullscreen mode

Nosso Service:

package com.leonardossev.dtoexemplo.service;

import com.leonardossev.dtoexemplo.model.entity.UsuarioEntity;
import com.leonardossev.dtoexemplo.repository.UsuarioRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UsuarioService {

    @Autowired
    private UsuarioRepository usuarioRepository;

    public UsuarioEntity salvarUsuario(UsuarioEntity usuario) {
        return this.usuarioRepository.save(usuario);
    }
}
Enter fullscreen mode Exit fullscreen mode

Nosso Controller:

package com.leonardossev.dtoexemplo.controller;

import com.leonardossev.dtoexemplo.model.entity.UsuarioEntity;
import com.leonardossev.dtoexemplo.service.UsuarioService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(path = "/usuario")
public class UsuarioController {

    @Autowired
    private UsuarioService usuarioService;

    @PostMapping
    public ResponseEntity<UsuarioEntity> salvarUsuario(final @RequestBody UsuarioEntity usuario) {
        return new ResponseEntity<UsuarioEntity>(this.usuarioService.salvarUsuario(usuario), HttpStatus.CREATED);
    }

}

Enter fullscreen mode Exit fullscreen mode

Executando nossa aplicação

Para testar o endpoint responsável por guardar o registro na base, utilizarei o Insomnia.

Lembrando que, se não especificarmos nenhuma porta, ao executar a nossa aplicação, ela estará disponível na porta 8080.

Requisição POST via Insomnia para localhost:8080/usuario

Note que estamos recebendo exatamente as informações que salvamos na nossa base de dados. Mas em uma aplicação real provavelmente não gostaríamos de receber todos os dados dessa forma.

Por exemplo: não iríamos querer que a senha do nosso usuário (sem entrar no mérito de criptografia) estivesse exposta dessa forma.

Outro exemplo: se quiséssemos trazer informações relacionadas ao usuário que não estão exatamente na tabela de usuário.

No nosso caso, o uso de DTO pode nos ajudar a solucionar essa questão de forma a nos permitir a escolher quais dados nós queiramos disponibilizar quando salvarmos um novo usuário.

Vamos adicionar a seguinte classe ao nosso projeto:

package com.leonardossev.dtoexemplo.model.dto;

public class UsuarioDTO {

    private Long id;

    private String nome;

    private String email;

    public UsuarioDTO(Long id, String nome, String email) {
        this.id = id;
        this.nome = nome;
        this.email = email;
    }

    // Getters e setters...

}

Enter fullscreen mode Exit fullscreen mode

Vamos também fazer os seguintes ajustes:

  • Implementar um método na classe UsuarioEntity que irá transformar as informações da classe UsuarioEntitypara informações da classe UsuarioDTO;
  • Fazer com que o nosso endpoint retorne um objeto da classe UsuarioDTO.

Implementando método para transformar informações

Na classe UsuarioEntity vamos criar o seguinte método:

    public UsuarioDTO obterUsuarioDTO() {
        return new UsuarioDTO(this.id, this.nome, this.email);
    }

Enter fullscreen mode Exit fullscreen mode

Alterando retorno do endpoint de salvar usuário

No nosso service, iremos ajustar a implementação do método salvarUsuario(UsuarioEntity usuario) para que ele possa retornar um objeto da classe UsuarioDTO:

    public UsuarioDTO salvarUsuario(UsuarioEntity usuario) {
        var usuarioSalvo = this.usuarioRepository.save(usuario);

        return usuarioSalvo.obterUsuarioDTO();
    }

Enter fullscreen mode Exit fullscreen mode

Além disso, é necessário que façamos ajustes no método do nosso controller:

@PostMapping
    public ResponseEntity<UsuarioDTO> salvarUsuario(final @RequestBody UsuarioEntity usuario) {
        return new ResponseEntity<UsuarioDTO>(this.usuarioService.salvarUsuario(usuario), HttpStatus.CREATED);
    }
Enter fullscreen mode Exit fullscreen mode

Dessa forma, quando executamos a aplicação e tentamos salvar um novo usuário, recebemos as seguintes informações:

Requisição POST via Insomnia para localhost:8080/usuario

Perceba que não estamos recebendo a informação da senha do usuário.

Considerações finais

O repositório com o exemplo do artigo pode ser encontrado aqui.

Referências

https://www.baeldung.com/hibernate-identifiers
https://stackabuse.com/data-transfer-object-pattern-in-java-implementation-and-mapping/

Em caso de dúvidas, estou sempre aberto a sugestões, críticas e ideias! o/

Oldest comments (0)