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...
}
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> {
}
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);
}
}
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);
}
}
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.
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...
}
Vamos também fazer os seguintes ajustes:
- Implementar um método na classe
UsuarioEntity
que irá transformar as informações da classeUsuarioEntity
para informações da classeUsuarioDTO
; - 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);
}
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();
}
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);
}
Dessa forma, quando executamos a aplicação e tentamos salvar um novo usuário, recebemos as seguintes informações:
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/
Top comments (0)