Objetivo: Refatorar a API para ser mais robusta e desacoplada, utilizando DTOs, tratamento de exceções centralizado e validação de entrada.
Tarefa) diretamente. Isso é uma má prática, pois acopla o contrato da sua API à estrutura do seu banco de dados.classDiagram
direction LR
TarefaController ..> TarefaService : delega para
TarefaService ..> TarefaRepository : usa
TarefaRepository ..> Tarefa : gerencia
TarefaService ..> TarefaRequestDTO : recebe
TarefaService ..> TarefaResponseDTO : retorna
class TarefaController {
+criar("TarefaRequestDTO") TarefaResponseDTO
+atualizar("Long, TarefaRequestDTO") TarefaResponseDTO
}
class TarefaService {
+criar("TarefaRequestDTO") TarefaResponseDTO
}
class Tarefa {
<<Entity>>
-Long id
-String descricao
-boolean concluida
}
class TarefaRequestDTO {
<<DTO>>
-String descricao
-Boolean concluida
}
class TarefaResponseDTO {
<<DTO>>
+Long id
+String descricao
+boolean concluida
}
src/main/java/br/com/curso/listadetarefas/api, crie um novo pacote dto.dto, crie as classes TarefaRequestDTO.java e TarefaResponseDTO.java.TarefaRequestDTO.java
package br.com.curso.listadetarefas.api.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
public record TarefaRequestDTO(
@NotBlank(message = "A descrição não pode ser vazia.")
@Size(min = 3, message = "A descrição deve ter no mínimo 3 caracteres.")
String descricao,
@NotNull(message = "O campo 'concluida' é obrigatório.")
Boolean concluida
) {}
TarefaResponseDTO.java
package br.com.curso.listadetarefas.api.dto;
public record TarefaResponseDTO(
Long id,
String descricao,
boolean concluida
) {}
listadetarefas.api/
└── src/main/java/br/com/curso/listadetarefas/api/
├── dto/
│ ├── TarefaRequestDTO.java
│ └── TarefaResponseDTO.java
├── tarefa/
│ └── ... (classes existentes)
└── ListadetarefasApiApplication.java
Agora, adaptamos o Service e o Controller para usar os novos DTOs.
TarefaService.java (Refatorado)
package br.com.curso.listadetarefas.api.tarefa;
import br.com.curso.listadetarefas.api.dto.TarefaRequestDTO;
import br.com.curso.listadetarefas.api.dto.TarefaResponseDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class TarefaService {
@Autowired private TarefaRepository repository;
public List<TarefaResponseDTO> listarTodas() {
return repository.findAll().stream().map(this::toResponseDTO).collect(Collectors.toList());
}
public TarefaResponseDTO criar(TarefaRequestDTO requestDTO) {
Tarefa tarefa = new Tarefa();
tarefa.setDescricao(requestDTO.descricao());
tarefa.setConcluida(requestDTO.concluida());
return toResponseDTO(repository.save(tarefa));
}
// ... outros métodos (atualizar, deletar) devem ser adaptados da mesma forma ...
private TarefaResponseDTO toResponseDTO(Tarefa tarefa) {
return new TarefaResponseDTO(tarefa.getId(), tarefa.getDescricao(), tarefa.isConcluida());
}
}
TarefaController.java (Refatorado)
package br.com.curso.listadetarefas.api.tarefa;
import br.com.curso.listadetarefas.api.dto.TarefaRequestDTO;
import br.com.curso.listadetarefas.api.dto.TarefaResponseDTO;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/tarefas")
@CrossOrigin(origins = "*")
public class TarefaController {
@Autowired private TarefaService service;
@GetMapping
public List<TarefaResponseDTO> listarTarefas() { return service.listarTodas(); }
@PostMapping
public TarefaResponseDTO criarTarefa(@Valid @RequestBody TarefaRequestDTO tarefaDTO) {
return service.criar(tarefaDTO);
}
// ... outros endpoints adaptados ...
}
@RestControllerAdvice para capturar exceções globalmente, limpando o controlador.exception e a classe RestExceptionHandler.java.RestExceptionHandler.java
package br.com.curso.listadetarefas.api.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class RestExceptionHandler {
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String handleNotFound(RuntimeException ex) {
return ex.getMessage();
}
}
try-catch podem ser removidos do TarefaController.