📚 Pré-requisitos Teóricos: este projeto aplica conceitos ensinados em Especialização em Backend com Java e Spring Boot e na Matriz de Aplicações Full Stack. Recomendado revisar antes de começar.

🚀 Projeto Lista de Tarefas (Parte 01): API REST & Interface Thymeleaf

Bem-vindo à Parte 01 da Trilha Prática de Aplicações Full Stack! Esta série é progressiva e acompanha a sua evolução ao longo dos módulos:

Nesta primeira etapa, o objetivo é compreender os fundamentos sem sobrecarga: você aprenderá o que é uma API, o que é REST, como o Spring Boot organiza o código em camadas (MVC) e como testar as operações de CRUD de forma 100% visual.


📐 1. Engenharia de Software: Levantamento de Requisitos e Rastreabilidade

Antes de escrever qualquer linha de código em Java, um engenheiro de software profissional realiza o levantamento de requisitos. Criar software sem entender o que deve ser feito e quais critérios definem o sucesso leva a retrabalho, bugs e frustração.

Neste projeto didático, aplicamos as melhores práticas da Engenharia de Software para que você aprenda a relacionar cada necessidade de negócio a uma classe ou método no código.

📝 1.1 Contexto de Negócio e Histórias de Usuário (User Stories)

Nosso cliente precisa de uma solução simples, centralizada e de alta performance para gerenciar afazeres cotidianos. No modelo ágil, expressamos essas necessidades no formato de Histórias de Usuário:


📋 1.2 Especificação de Requisitos Funcionais (RF)

Os Requisitos Funcionais descrevem o que o sistema deve fazer, isto é, suas funcionalidades e comportamentos observáveis:

ID Nome do Requisito Descrição Detalhada Regra de Negócio Associada
RF-01 Cadastrar Tarefa O sistema deve criar um novo registro de tarefa a partir de uma descrição textual enviada. A tarefa recém-criada deve iniciar com status concluida = false. O identificador (id) deve ser gerado automaticamente.
RF-02 Listar Todas as Tarefas O sistema deve retornar a coleção completa de tarefas cadastradas. Se o banco estiver vazio, deve retornar uma lista vazia ([]) com status 200 OK.
RF-03 Consultar Tarefa por ID O sistema deve permitir a busca pontual de uma tarefa específica pelo seu ID. Se o ID não existir, retornar erro 404 Not Found.
RF-04 Atualizar Tarefa O sistema deve permitir alterar a descrição e/ou o status de conclusão de uma tarefa existente. Se o ID informado não existir na base, a operação deve ser rejeitada com 404 Not Found.
RF-05 Alternar Status (Toggle) O sistema deve fornecer uma operação simplificada para inverter o status de conclusão (true ⇄ false). Útil para checkboxes e ações de um clique em interfaces gráficas.
RF-06 Excluir Tarefa O sistema deve remover permanentemente uma tarefa a partir do seu identificador numérico. Se o ID existir, remover e responder com 204 No Content. Se não existir, responder com 404 Not Found.

🛡️ 1.3 Especificação de Requisitos Não-Funcionais (RNF)

Os Requisitos Não-Funcionais definem como o sistema deve se comportar, estabelecendo critérios de qualidade, arquitetura, usabilidade e tecnologia:

ID Categoria Descrição do Critério Justificativa Pedagógica & Técnica
RNF-01 Arquitetura RESTful A comunicação externa deve adotar o protocolo HTTP com URIs semânticas (/api/tarefas) e payloads JSON padrão. Permite que qualquer cliente (web, mobile, desktop) consuma a mesma API sem acoplamento.
RNF-02 Separação de Camadas O código deve seguir estritamente o padrão em camadas: Controller (exposição), Service (lógica), Repository (dados) e Entity (domínio). Evita código espaguete e ensina a arquitetura corporativa padrão de mercado.
RNF-03 Persistência Volátil (Zero-Config) O sistema deve operar com banco de dados relacional em memória (H2 Database), sem exigir instalação de servidores MySQL/PostgreSQL locais. Elimina barreiras de ambiente para alunos iniciantes; o banco sobe e desce com a aplicação.
RNF-04 Dupla Interface (SSR + API) A aplicação deve fornecer uma interface web renderizada no servidor (Thymeleaf) e uma API REST JSON pura no mesmo artefato executável. Serve de ponte pedagógica para o aluno visualizar a aplicação funcionando antes de avançar para SPAs complexas como Angular.
RNF-05 Feedback HTTP Semântico Todas as respostas da API devem utilizar os códigos de status HTTP corretos (200 OK, 201 Created, 204 No Content, 404 Not Found). Garante aderência às boas práticas de Engenharia de Software para serviços web.

🎭 1.4 Diagrama de Casos de Uso (UML Use Case)

O diagrama abaixo sintetiza como os atores (usuário via navegador ou cliente REST YARC) interagem com as fronteiras do sistema:

flowchart LR
    User(["👤 Aluno / Usuário"])
    
    subgraph Sistema ["📦 Sistema de Lista de Tarefas (Spring Boot)"]
        direction TB
        UC1(["UC-01: Cadastrar Tarefa (RF-01)"])
        UC2(["UC-02: Listar Tarefas (RF-02)"])
        UC3(["UC-03: Consultar por ID (RF-03)"])
        UC4(["UC-04: Atualizar Tarefa (RF-04)"])
        UC5(["UC-05: Alternar Conclusão (RF-05)"])
        UC6(["UC-06: Excluir Tarefa (RF-06)"])
        
        UC7(["UC-07: Inspecionar no H2 Console"])
    end
    
    User --> UC1
    User --> UC2
    User --> UC3
    User --> UC4
    User --> UC5
    User --> UC6
    User -.->|Perfil Admin| UC7

    style Sistema fill:#f8fafc,stroke:#334155,stroke-width:2px
    style UC1 fill:#e0f2fe,stroke:#0284c7
    style UC2 fill:#e0f2fe,stroke:#0284c7
    style UC3 fill:#e0f2fe,stroke:#0284c7
    style UC4 fill:#e0f2fe,stroke:#0284c7
    style UC5 fill:#e0f2fe,stroke:#0284c7
    style UC6 fill:#fee2e2,stroke:#ef4444
    style UC7 fill:#fef3c7,stroke:#f59e0b

🗺️ 1.5 Matriz de Rastreabilidade de Requisitos (RTM)

A Matriz de Rastreabilidade garante que nenhum requisito fique sem implementação e que cada linha de código tenha uma justificativa clara de existência:

Requisito Camada de Domínio Camada de Dados Camada de Serviço Exposição REST (API) Interface Web (Thymeleaf) Verificação / Teste
RF-01 Tarefa.java TarefaRepository.save() TarefaService.criar() POST /api/tarefas Formulário <form method="post"> YARC: Teste POST (Status 201)
RF-02 Tarefa.java TarefaRepository.findAll() TarefaService.listarTodas() GET /api/tarefas Tabela th:each="t : ${tarefas}" YARC: Teste GET (Status 200)
RF-03 Tarefa.java TarefaRepository.findById() TarefaService.buscarPorId() GET /api/tarefas/{id} Não aplicável nesta fase YARC: Teste GET/{id} (Status 200/404)
RF-04 Tarefa.java TarefaRepository.save() TarefaService.atualizar() PUT /api/tarefas/{id} Modal / formulário de edição YARC: Teste PUT (Status 200)
RF-05 Tarefa.java TarefaRepository.save() TarefaService.alternarStatus() PUT /api/tarefas/{id} Link /tarefas/{id}/alternar Clique visual no checkbox/botão
RF-06 Tarefa.java TarefaRepository.deleteById() TarefaService.deletar() DELETE /api/tarefas/{id} Link /tarefas/{id}/deletar YARC: Teste DELETE (Status 204)
RNF-01 - - - @RestController + JSON - Inspecionar cabeçalho Content-Type
RNF-02 @Entity @Repository @Service @RestController @Controller Inspeção visual da árvore de pacotes
RNF-03 - spring-boot-starter-data-jpa - - - Acesso ao /h2-console
RNF-04 - - - TarefaRestController TarefaWebController + HTML Acesso a /tarefas no navegador

🗺️ 2. Arquitetura da Aplicação e Visão Sistêmica

Neste projeto, tudo roda dentro de um único serviço Spring Boot na porta 8080, oferecendo dois pontos de acesso que compartilham as mesmas regras de negócio:

flowchart TD
    subgraph SpringBoot ["⚙️ Backend Spring Boot (Porta 8080)"]
        direction TB
        Service["🧠 TarefaService<br>(Regras de Negócio e CRUD)"]
        Repo["🗄️ TarefaRepository<br>(Spring Data JPA)"]
        DB[("💾 Banco H2<br>(Em Memória - testdb)")]
        
        API_Ctrl["🔌 TarefaRestController<br>(@RestController - /api/tarefas)"]
        Web_Ctrl["🖥️ TarefaWebController<br>(@Controller - /tarefas)"]
        
        API_Ctrl -->|Chama| Service
        Web_Ctrl -->|Chama| Service
        Service -->|Consulta/Salva| Repo
        Repo -->|SQL Automático| DB
    end

    subgraph Clientes ["👥 Como Você Irá Interagir"]
        YARC["🧩 Extensão YARC (Chrome)<br>Dispara requisições e inspeciona JSON"]
        Thymeleaf["🌐 Navegador Web<br>Interface gráfica HTML renderizada no servidor"]
        H2Console["🔍 H2 Web Console<br>Visualiza a tabela TB_TAREFAS no banco"]
    end

    YARC <-->|HTTP JSON| API_Ctrl
    Thymeleaf <-->|HTTP HTML| Web_Ctrl
    H2Console -.->|SQL Direto| DB

    style SpringBoot fill:#f9fbe7,stroke:#827717,stroke-width:2px
    style Clientes fill:#e1f5fe,stroke:#0277bd,stroke-width:2px
  1. API REST (/api/tarefas): Entrega dados puros em formato JSON. Ideal para clientes móveis, SPAs e integrações externas.
  2. Interface Web (/tarefas): Renderiza páginas HTML dinâmicas com Thymeleaf diretamente no servidor (Server-Side Rendering).
  3. Console do Banco H2 (/h2-console): Interface administrativa para inspecionar as tabelas criadas na memória RAM.

🧠 3. Teoria Descomplicada: O que você precisa saber

Antes de abrir o código, vamos esclarecer os quatro conceitos fundamentais da web moderna.

🍽️ A Analogia do Restaurante: O que é uma API?

Imagine que você está em um restaurante:

A Analogia do Restaurante e a Arquitetura de uma API REST

📦 O que é JSON?

JSON (JavaScript Object Notation) é o padrão universal que sistemas usam para conversar pela internet. É um formato de texto leve, legível por humanos e facilmente interpretado por qualquer linguagem (Java, TypeScript, Python, etc.).

Exemplo de uma tarefa representada em JSON:

{
  "id": 1,
  "descricao": "Aprender Spring Boot e REST",
  "concluida": false
}

🚦 Os 4 Verbos do CRUD

Toda aplicação de gerenciamento de dados realiza quatro operações básicas (Create, Read, Update, Delete). Na web, associamos cada operação a um método HTTP:

Operação Verbo HTTP Ação na API Analogia
Create (Criar) POST Cadastra uma nova tarefa Enviar um formulário preenchido
Read (Ler) GET Consulta uma ou mais tarefas Olhar a lista de tarefas
Update (Atualizar) PUT Atualiza os dados de uma tarefa existente Editar o texto ou marcar concluída
Delete (Excluir) DELETE Remove uma tarefa do banco Rasgar a anotação da tarefa

🏛️ Por que dividimos o código em Camadas?

No padrão arquitetural do Spring Boot, cada classe tem uma única responsabilidade:

Arquitetura em Camadas do Spring Boot


⚙️ 4. Construção do Backend Passo a Passo (Tutorial Autoguiado)

Neste tutorial prático autoguiado, cada passo possui um Ponto de Verificação (Checkpoint). Não avance para a etapa seguinte sem confirmar que o checkpoint atual foi atendido com sucesso.

📂 Passo 1: Criação e Validação do Projeto no Spring Initializr

🔍 1.1 Verificação Prévia Obrigatória da Versão do Java

Antes de gerar o projeto, abra o seu terminal (Prompt de Comando ou PowerShell no Windows) e verifique qual versão do JDK está instalada no seu computador:

java --version

A saída mostrará a versão ativa na sua máquina, por exemplo:

⚠️ Regra Fundamental de Compatibilidade: A versão do Java que você selecionar no Spring Initializr deve ser igual ou inferior à versão instalada na sua máquina! Se você selecionar Java 21 no site mas sua máquina só tiver o Java 17 instalado, a aplicação apresentará erro de compilação (UnsupportedClassVersionError).


🌐 1.2 Configuração dos Parâmetros no Spring Initializr

Acesse start.spring.io e configure os campos exatamente como indicado:

Dependências necessárias (adicione clicando em “ADD DEPENDENCIES”):

  1. Spring Web (criação de endpoints REST e MVC)
  2. Spring Data JPA (persistência e mapeamento objeto-relacional)
  3. Thymeleaf (motor de templates para a interface web)
  4. H2 Database (banco de dados relacional em memória)
  5. Lombok (geração automática de getters, setters e construtores)

Clique em GENERATE (ou pressione Ctrl + Enter) para baixar o arquivo compactado api-spring.zip.


🗜️ 1.3 Como Descompactar Corretamente no Windows (Evite Pastas Duplicadas)

Um dos erros mais comuns de quem está começando no Windows é criar “pastas gêmeas” ao extrair o .zip (como api-spring\api-spring):

  1. Localize o arquivo baixado api-spring.zip na sua pasta Downloads.
  2. Clique com o botão direito nele e escolha Extrair Tudo….
  3. Escolha uma pasta de desenvolvimento organizada no seu computador (por exemplo: C:\Projetos\ ou na pasta do curso).
  4. Verificação visual obrigatória: Ao entrar na pasta descompactada, você deve ver diretamente os arquivos pom.xml, mvnw.cmd e a pasta src/. Se você abrir a pasta e encontrar outra pasta com o mesmo nome dentro dela, mova o conteúdo para que o pom.xml fique na raiz!

Arquivo: pom.xml

O arquivo pom.xml gerado contém as dependências fundamentais:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>4.0.0</version> <!-- Ou a versao estavel 4.0.x / 4.1.x selecionada no site -->
		<relativePath/>
	</parent>
	<groupId>br.com.curso</groupId>
	<artifactId>api-spring</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>api-spring</name>
	<description>API REST e Interface Web Thymeleaf para Lista de Tarefas</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>

🎯 Checkpoint de Verificação 1: Integridade da Fundação Maven


🏛️ 3.2 Anatomia e Organização de um Projeto Spring Boot

O Spring Boot adota o padrão canônico do ecossistema Maven (Maven Standard Directory Layout). Cada pasta tem um papel estrito e fundamental:

Pasta / Arquivo Para que serve? Regra de Ouro
pom.xml O arquivo de manifesto do Maven. Fica sempre na raiz do projeto. Declara dependências e versão do Java.
mvnw e mvnw.cmd O Maven Wrapper executável. Permite compilar e rodar o projeto sem que você precise instalar o Maven no Windows.
src/main/java/ Onde mora todo o seu código-fonte Java. Qualquer arquivo .java da sua aplicação DEVE ficar aqui dentro.
src/main/resources/ Recursos de configuração e templates. Contém o application.properties e a pasta templates/ do Thymeleaf.
src/test/java/ Testes automatizados. Onde ficam as classes de teste de validação.

🌳 Estrutura Inicial Logo Após Descompactar o Projeto:

api-spring/
├── pom.xml                                           ← Configurações e dependências Maven
├── mvnw                                              ← Script de execução para Linux/macOS
├── mvnw.cmd                                          ← Script de execução para Windows
├── .mvn/wrapper/maven-wrapper.properties
└── src/
    ├── main/
    │   ├── java/
    │   │   └── br/com/curso/listadetarefas/api/      ← Pacote Raiz da Aplicação
    │   │       └── ListadetarefasApiApplication.java ← Classe Principal com o método main
    │   └── resources/
    │       └── application.properties                ← Arquivo de propriedades e configuração
    └── test/
        └── java/
            └── br/com/curso/listadetarefas/api/
                └── ListadetarefasApiApplicationTests.java ← Teste básico de contexto

🚨 A Regra de Ouro do Pacote Raiz e Component Scanning

Observe com muita atenção onde fica o arquivo ListadetarefasApiApplication.java:

⛔ O Erro nº 1 de Quem Começa com Spring Boot: Se você criar acidentalmente um pacote “fora” ou “ao lado” do pacote raiz (por exemplo: br.com.curso.tarefa em vez de br.com.curso.listadetarefas.api.tarefa), o Spring Boot NÃO encontrará suas anotações @RestController, @Service ou @Repository. A aplicação subirá sem erros, mas quando você tentar acessar http://localhost:8080/api/tarefas, receberá um erro 404 Not Found! Regra: Todos os seus novos pacotes devem ser criados dentro de br.com.curso.listadetarefas.api.


💻 3.3 Como Abrir o Projeto na sua IDE no Windows

Opção A: No Visual Studio Code (VS Code)

Para ter a melhor experiência de desenvolvimento no VS Code, utilizaremos o pacote oficial da VMware:

  1. Instalação do Spring Boot Extension Pack (VMware):
    • Pressione o atalho Ctrl + Shift + X no Windows (ou clique no ícone de blocos/extensões na barra lateral esquerda).
    • Digite na busca: Spring Boot Extension Pack.
    • Verifique o autor: deve ser VMware (ou Broadcom / Pivotal).
    • Clique em Instalar (Install).
    • (Nota: Este pacote já instala automaticamente o Spring Boot Tools, o Spring Initializr Java Support, o Spring Boot Dashboard e se integra com o Extension Pack for Java da Microsoft).
  2. Por que essa extensão é essencial para o aluno iniciante?
    • 🌱 Spring Boot Dashboard: Adiciona um painel visual exclusivo na barra lateral com o ícone do Spring. Você pode iniciar (▶️) ou parar (⏹️) o servidor com um único clique do mouse, sem precisar memorizar comandos no terminal!
    • 💡 Auto-completar no application.properties: Conforme você digita spring.h2... ou server.port..., a extensão sugere os nomes válidos e exibe uma caixa explicando o que cada propriedade faz.
    • 🔗 Navegação de Endpoints: Ao lado de anotações como @GetMapping("/api/tarefas"), a extensão exibe links diretos para testar o endpoint no navegador.
  3. Abrindo a Pasta do Projeto:
    • Clique em Arquivo > Abrir Pasta… (ou File > Open Folder...).
    • Selecione a pasta api-spring (aquela que contém o arquivo pom.xml).
    • Aguarde a Indexação: Na barra inferior azul do VS Code, você verá a mensagem “Building workspace…” ou “Importing Maven project”. Aguarde essa mensagem sumir antes de criar arquivos.
  4. Painéis Úteis no Explorer do VS Code:
    • Explorer de Arquivos: Mostra as pastas físicas do Windows.
    • Java Projects: Mostra a árvore lógica de pacotes Java com o ícone de xícara de café.
    • Spring Boot Dashboard: Painel dedicado para gerenciar a execução da sua API.

Opção B: No IntelliJ IDEA (Community ou Ultimate)

  1. Abra o IntelliJ IDEA.
  2. Na tela inicial (ou no menu superior), clique em File > Open….
  3. Navegue até a pasta do projeto e selecione a pasta api-spring (ou selecione diretamente o arquivo pom.xml).
  4. Clique em Open as Project (e clique em Trust Project se solicitado).
  5. No canto inferior direito, o IntelliJ exibirá uma barra de progresso baixando as dependências do Maven. Aguarde o processo terminar.
  6. A árvore do projeto à esquerda mostrará a pasta src/main/java com um pequeno ícone azul de pacote, indicando que ela foi reconhecida como raiz de código-fonte.

📁 3.4 Como Criar Pacotes e Classes nas IDEs (Notação de Ponto vs. Pastas)

No Java, um pacote é escrito com notação de ponto: br.com.curso.listadetarefas.api.tarefa.
No disco rígido do Windows, isso corresponde a uma hierarquia de pastas aninhadas: src\main\java\br\com\curso\listadetarefas\api\tarefa\

As IDEs modernas facilitam esse trabalho para que você não precise criar pasta por pasta manualmente.

Como Criar o Subpacote tarefa:

Como Criar uma Classe ou Interface:



⚙️ Passo 2: Configuração do application.properties

O arquivo de configuração do Spring Boot fica sempre dentro de src/main/resources/. Localize-o na sua IDE e abra para edição:

🌳 Onde este arquivo fica localizado:

api-spring/
└── src/
    └── main/
        └── resources/
            └── application.properties  ← [Configuração do Banco e Servidor]

Substitua todo o conteúdo do arquivo pelas configurações abaixo:

spring.application.name=api-spring

# Permite conexoes locais na porta padrao 8080
server.address=0.0.0.0
server.port=8080

# Habilita o console web do H2
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

# Configuracoes de conexao com o banco H2 em memoria
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# Dialeto Hibernate e inicializacao JPA
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.defer-datasource-initialization=true

# Desabilita cache do Thymeleaf para atualizar paginas sem reiniciar
spring.thymeleaf.cache=false

🎯 Checkpoint de Verificação 2: Banco de Dados Volátil H2 e Porta do Servidor


📝 Passo 3: Modelagem da Entidade (Tarefa.java)

Agora começaremos a criar as classes de negócio da aplicação.

📊 Modelagem de Dados: Diagrama de Classes UML & Modelo Entidade-Relacionamento (ERD)

Antes de escrever o código Java, vejamos a representação formal da arquitetura de dados segundo os padrões da Engenharia de Software:

classDiagram
    direction TB
    class Tarefa {
        <<Entity>>
        -Long id
        -String descricao
        -Boolean concluida
        +getId() Long
        +setId(Long id) void
        +getDescricao() String
        +setDescricao(String descricao) void
        +isConcluida() Boolean
        +setConcluida(Boolean concluida) void
    }
    note for Tarefa "Mapeada para a tabela 'tb_tarefas' via Jakarta Persistence (JPA)"
erDiagram
    TB_TAREFAS {
        bigint id PK "Identificador único (Auto Increment)"
        varchar(255) descricao "Texto da tarefa (NOT NULL)"
        boolean concluida "Estado de conclusão (DEFAULT false)"
    }

🛠️ Como criar o arquivo na sua IDE:

  1. Se ainda não criou, crie o subpacote tarefa dentro de br.com.curso.listadetarefas.api:
    • IntelliJ: Botão direito em br.com.curso.listadetarefas.api $\rightarrow$ New > Package $\rightarrow$ digite tarefa.
    • VS Code: Botão direito na pasta api $\rightarrow$ New Folder $\rightarrow$ digite tarefa.
  2. Dentro do pacote tarefa, crie o arquivo Tarefa.java:
    • IntelliJ: Botão direito no pacote tarefa $\rightarrow$ New > Java Class $\rightarrow$ Tarefa.
    • VS Code: Botão direito na pasta tarefa $\rightarrow$ New File $\rightarrow$ Tarefa.java.

🌳 Como deve ficar a sua estrutura após este passo:

src/main/java/br/com/curso/listadetarefas/api/
├── ListadetarefasApiApplication.java
└── tarefa/
    └── Tarefa.java  ← [NOVO: Entidade JPA / Tabela tb_tarefas]

Código Completo: Tarefa.java

package br.com.curso.listadetarefas.api.tarefa;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "tb_tarefas")
public class Tarefa {

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

    private String descricao;

    private boolean concluida;
}

🎯 Checkpoint de Verificação 3: Mapeamento Objeto-Relacional (ORM)


🗄️ Passo 4: O Repositório JPA (TarefaRepository.java)

O repositório é uma Interface (e não uma classe comum) responsável por executar as operações no banco.

🛠️ Como criar o arquivo na sua IDE:

🌳 Como deve ficar a sua estrutura após este passo:

src/main/java/br/com/curso/listadetarefas/api/
├── ListadetarefasApiApplication.java
└── tarefa/
    ├── Tarefa.java
    └── TarefaRepository.java  ← [NOVO: Interface Spring Data JPA]

Código Completo: TarefaRepository.java

package br.com.curso.listadetarefas.api.tarefa;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface TarefaRepository extends JpaRepository<Tarefa, Long> {
}

💡 A Mágica do Spring Data: Ao estender JpaRepository<Tarefa, Long>, você ganha de graça métodos como findAll(), findById(), save(), deleteById() e existsById(), sem escrever uma única linha de SQL!

🎯 Checkpoint de Verificação 4: Abstração da Camada de Persistência


🧠 Passo 5: A Camada de Serviço (TarefaService.java)

A camada de serviço é uma classe comum do Java anotada com @Service. Ela reúne as regras de negócio da nossa lista de tarefas.

🛠️ Como criar o arquivo na sua IDE:

🌳 Como deve ficar a sua estrutura após este passo:

src/main/java/br/com/curso/listadetarefas/api/
├── ListadetarefasApiApplication.java
└── tarefa/
    ├── Tarefa.java
    ├── TarefaRepository.java
    └── TarefaService.java  ← [NOVO: Camada de Serviço e Regras de Negócio]

Código Completo: TarefaService.java

package br.com.curso.listadetarefas.api.tarefa;

import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class TarefaService {

    private final TarefaRepository tarefaRepository;

    // Injecao de dependencia via construtor (boa pratica recomendada)
    public TarefaService(TarefaRepository tarefaRepository) {
        this.tarefaRepository = tarefaRepository;
    }

    public List<Tarefa> listarTodas() {
        return tarefaRepository.findAll();
    }

    public Optional<Tarefa> buscarPorId(Long id) {
        return tarefaRepository.findById(id);
    }

    public Tarefa criar(Tarefa tarefa) {
        return tarefaRepository.save(tarefa);
    }

    public Optional<Tarefa> atualizar(Long id, Tarefa dadosAtualizados) {
        return tarefaRepository.findById(id).map(tarefaExistente -> {
            tarefaExistente.setDescricao(dadosAtualizados.getDescricao());
            tarefaExistente.setConcluida(dadosAtualizados.isConcluida());
            return tarefaRepository.save(tarefaExistente);
        });
    }

    public boolean deletar(Long id) {
        if (tarefaRepository.existsById(id)) {
            tarefaRepository.deleteById(id);
            return true;
        }
        return false;
    }

    public Optional<Tarefa> alternarStatus(Long id) {
        return tarefaRepository.findById(id).map(tarefa -> {
            tarefa.setConcluida(!tarefa.isConcluida());
            return tarefaRepository.save(tarefa);
        });
    }
}

🎯 Checkpoint de Verificação 5: Regras de Negócio e Encapsulamento


🔌 Passo 6: O Controlador REST (TarefaRestController.java)

Agora criaremos o controlador REST, responsável por expor as rotas HTTP e devolver respostas puras em JSON para clientes como a extensão YARC.

🛠️ Como criar o arquivo na sua IDE:

🌳 Como deve ficar a sua estrutura após este passo:

src/main/java/br/com/curso/listadetarefas/api/
├── ListadetarefasApiApplication.java
└── tarefa/
    ├── Tarefa.java
    ├── TarefaRepository.java
    ├── TarefaService.java
    └── TarefaRestController.java  ← [NOVO: Endpoints REST JSON /api/tarefas]

Código Completo: TarefaRestController.java

package br.com.curso.listadetarefas.api.tarefa;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/tarefas")
@CrossOrigin(origins = "*")
public class TarefaRestController {

    private final TarefaService tarefaService;

    public TarefaRestController(TarefaService tarefaService) {
        this.tarefaService = tarefaService;
    }

    @GetMapping
    public List<Tarefa> listar() {
        return tarefaService.listarTodas();
    }

    @GetMapping("/{id}")
    public ResponseEntity<Tarefa> buscarPorId(@PathVariable Long id) {
        return tarefaService.buscarPorId(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    public ResponseEntity<Tarefa> criar(@RequestBody Tarefa tarefa) {
        Tarefa salva = tarefaService.criar(tarefa);
        return ResponseEntity.status(HttpStatus.CREATED).body(salva);
    }

    @PutMapping("/{id}")
    public ResponseEntity<Tarefa> atualizar(@PathVariable Long id, @RequestBody Tarefa tarefa) {
        return tarefaService.atualizar(id, tarefa)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deletar(@PathVariable Long id) {
        if (tarefaService.deletar(id)) {
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.notFound().build();
    }
}

🎯 Checkpoint de Verificação 6: Exposição da API REST Semântica


🧩 5. Testando a API Visualmente com a Extensão YARC (Validação dos RFs)

Em vez de memorizar comandos complexos de terminal no início, vamos usar uma ferramenta visual e intuitiva no próprio navegador para validar cada um dos Requisitos Funcionais (RF-01 a RF-06).

📥 4.1 Instalação do YARC

  1. Abra o navegador Google Chrome (ou Edge / Brave).
  2. Acesse a Chrome Web Store e procure por YARC (Yet Another REST Client) (ou instale diretamente pela loja de extensões).
  3. Clique em Usar no Chrome para ativar a extensão.
  4. Clique no ícone de quebra-cabeça no canto superior direito do navegador e fixe o YARC na barra de ferramentas.

🧪 4.2 Executando o CRUD Passo a Passo no YARC

Inicie a aplicação Spring Boot (veja a seção Como Executar) e abra o YARC.

Ciclo de Vida de uma Requisição CRUD

1️⃣ Teste GET — Listar Tarefas (Inicial)


2️⃣ Teste POST — Criar a Primeira Tarefa


3️⃣ Teste POST — Criar a Segunda Tarefa


4️⃣ Teste GET — Listar Novamente


5️⃣ Teste PUT — Atualizar Tarefa

Vamos marcar a Tarefa 1 como concluída:


6️⃣ Teste DELETE — Excluir Tarefa

Vamos remover a Tarefa 2:


🔍 4.3 Verificando os Dados no Console H2

Para comprovar que tudo o que você fez no YARC foi gravado no banco de dados real:

  1. Abra uma nova aba no navegador e acesse: http://localhost:8080/h2-console
  2. No campo JDBC URL, digite exatamente: jdbc:h2:mem:testdb
  3. No campo User Name, confirme: sa
  4. Deixe o campo Password em branco e clique em Connect.
  5. No painel SQL, digite:
    SELECT * FROM TB_TAREFAS;
    
  6. Clique em Run. Você verá a tabela com as linhas atualizadas diretamente no banco em memória!

🧪 4.4 Matriz de Homologação & Checkpoints de Validação da API REST

Checkpoint Operação / Ação de Teste Critério de Aceite Status
CP-01 GET /api/tarefas (Carga Inicial) Status HTTP 200 OK, retornando array JSON vazio []. PASS ✅
CP-02 POST /api/tarefas (Nova Tarefa) Status HTTP 201 Created, corpo com id: 1 gerado pelo H2. PASS ✅
CP-03 GET /api/tarefas (Listagem com Dados) Status HTTP 200 OK, lista contendo as tarefas cadastradas. PASS ✅
CP-04 PUT /api/tarefas/1 (Atualização / Conclusão) Status HTTP 200 OK, objeto retornado com concluida: true. PASS ✅
CP-05 DELETE /api/tarefas/2 (Exclusão) Status HTTP 204 No Content sem corpo de resposta. PASS ✅
CP-06 Auditoria SQL no H2 Console Tabela TB_TAREFAS com dados 100% íntegros e consistentes. PASS ✅

🖥️ 5. Interface Gráfica com Thymeleaf (O Fechamento Visual)

Agora que você já compreendeu como a API REST opera com JSON, vamos construir uma interface visual amigável na mesma aplicação, usando Thymeleaf.

🎮 Por que Thymeleaf antes dos Frontends Desacoplados (Svelte, Vue, React, Angular)?


📄 Passo 7: O Controlador Web (TarefaWebController.java)

Agora que a API REST está pronta e testada no YARC, criaremos o controlador web responsável por entregar páginas HTML renderizadas no servidor pelo Thymeleaf.

🛠️ Como criar o arquivo na sua IDE:

🌳 Como deve ficar a sua estrutura após este passo:

src/main/java/br/com/curso/listadetarefas/api/
├── ListadetarefasApiApplication.java
└── tarefa/
    ├── Tarefa.java
    ├── TarefaRepository.java
    ├── TarefaService.java
    ├── TarefaRestController.java
    └── TarefaWebController.java  ← [NOVO: Controlador Web Thymeleaf /tarefas]

Código Completo: TarefaWebController.java

package br.com.curso.listadetarefas.api.tarefa;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/tarefas")
public class TarefaWebController {

    private final TarefaService tarefaService;

    public TarefaWebController(TarefaService tarefaService) {
        this.tarefaService = tarefaService;
    }

    @GetMapping
    public String listarTarefas(Model model) {
        model.addAttribute("tarefas", tarefaService.listarTodas());
        model.addAttribute("novaTarefa", new Tarefa());
        return "tarefas"; // Nome do template: tarefas.html
    }

    @PostMapping
    public String criarTarefa(@ModelAttribute("novaTarefa") Tarefa tarefa) {
        if (tarefa.getDescricao() != null && !tarefa.getDescricao().trim().isEmpty()) {
            tarefaService.criar(tarefa);
        }
        return "redirect:/tarefas";
    }

    @PostMapping("/{id}/alternar")
    public String alternarStatus(@PathVariable Long id) {
        tarefaService.alternarStatus(id);
        return "redirect:/tarefas";
    }

    @PostMapping("/{id}/excluir")
    public String excluirTarefa(@PathVariable Long id) {
        tarefaService.deletar(id);
        return "redirect:/tarefas";
    }
}

🎨 Passo 8: O Template HTML (src/main/resources/templates/tarefas.html)

O Thymeleaf busca automaticamente seus arquivos HTML dentro da pasta src/main/resources/templates/.

🛠️ Como criar a pasta e o arquivo HTML na sua IDE:

🌳 Como deve ficar a sua pasta resources:

src/main/resources/
├── application.properties
└── templates/
    └── tarefas.html  ← [NOVO: Página HTML com Thymeleaf e Bootstrap 5]

Código Completo: tarefas.html

<!DOCTYPE html>
<html lang="pt-BR" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lista de Tarefas — Spring Boot + Thymeleaf</title>
    <!-- Bootstrap 5 CSS via CDN -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body { background-color: #f8f9fa; }
        .card-custom { border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); }
        .completed-task { text-decoration: line-through; color: #6c757d; }
    </style>
</head>
<body>
    <div class="container py-5" style="max-width: 800px;">
        <div class="text-center mb-4">
            <h1 class="display-6 fw-bold text-primary">📝 Lista de Tarefas</h1>
            <p class="text-muted">
                Interface renderizada no servidor (SSR) com <strong>Thymeleaf</strong> e integrada ao mesmo <code>TarefaService</code> da API REST.
            </p>
        </div>

        <!-- Formulario de Cadastro -->
        <div class="card card-custom mb-4 border-0">
            <div class="card-body p-4">
                <h5 class="card-title fw-semibold mb-3">➕ Adicionar Nova Tarefa</h5>
                <form th:action="@{/tarefas}" th:object="${novaTarefa}" method="post" class="d-flex gap-2">
                    <input type="text" th:field="*{descricao}" class="form-control form-control-lg" 
                           placeholder="O que você precisa fazer?" required autofocus>
                    <button type="submit" class="btn btn-primary btn-lg px-4">Adicionar</button>
                </form>
            </div>
        </div>

        <!-- Tabela de Tarefas -->
        <div class="card card-custom border-0 mb-4">
            <div class="card-body p-4">
                <h5 class="card-title fw-semibold mb-3">📋 Minhas Tarefas</h5>
                
                <div th:if="${#lists.isEmpty(tarefas)}" class="alert alert-info mb-0">
                    ℹ️ Nenhuma tarefa cadastrada ainda! Adicione uma tarefa acima ou use o <strong>YARC</strong> para enviar um <code>POST</code> na API.
                </div>

                <div th:unless="${#lists.isEmpty(tarefas)}" class="table-responsive">
                    <table class="table table-hover align-middle mb-0">
                        <thead class="table-light">
                            <tr>
                                <th style="width: 80px;">#</th>
                                <th>Descrição</th>
                                <th style="width: 130px;">Status</th>
                                <th style="width: 180px;" class="text-end">Ações</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr th:each="tarefa : ${tarefas}">
                                <td class="fw-bold text-muted" th:text="${tarefa.id}">1</td>
                                <td>
                                    <span th:text="${tarefa.descricao}" 
                                          th:classappend="${tarefa.concluida} ? 'completed-task' : ''">
                                        Descrição
                                    </span>
                                </td>
                                <td>
                                    <span th:if="${tarefa.concluida}" class="badge bg-success">Concluída</span>
                                    <span th:unless="${tarefa.concluida}" class="badge bg-warning text-dark">Pendente</span>
                                </td>
                                <td class="text-end">
                                    <div class="d-inline-flex gap-1">
                                        <form th:action="@{/tarefas/{id}/alternar(id=${tarefa.id})}" method="post" class="d-inline">
                                            <button type="submit" class="btn btn-sm btn-outline-secondary" 
                                                    th:text="${tarefa.concluida ? 'Reabrir' : 'Concluir'}">
                                                Concluir
                                            </button>
                                        </form>
                                        <form th:action="@{/tarefas/{id}/excluir(id=${tarefa.id})}" method="post" class="d-inline">
                                            <button type="submit" class="btn btn-sm btn-outline-danger">Excluir</button>
                                        </form>
                                    </div>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>

        <!-- Links Uteis -->
        <div class="row g-3">
            <div class="col-md-6">
                <div class="p-3 bg-white card-custom text-center">
                    <h6 class="fw-bold text-secondary">🔌 API REST (JSON)</h6>
                    <a href="/api/tarefas" target="_blank" class="btn btn-sm btn-outline-primary mt-1">Acessar /api/tarefas</a>
                </div>
            </div>
            <div class="col-md-6">
                <div class="p-3 bg-white card-custom text-center">
                    <h6 class="fw-bold text-secondary">🗄️ Console do H2</h6>
                    <a href="/h2-console" target="_blank" class="btn btn-sm btn-outline-secondary mt-1">Abrir /h2-console</a>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

🎯 Checkpoint de Verificação 7: Interface Gráfica Monolítica e Renderização Server-Side


🖥️ 6. Testando a Interface Web com Thymeleaf (Validação Visual)

🌐 6.1 Testando o Fluxo Completo na Tela

  1. Com a aplicação rodando, acesse no navegador: http://localhost:8080/tarefas
  2. Digite uma nova tarefa (ex: “Comprar café”) e clique em Adicionar. A página recarrega instantaneamente mostrando o novo item com status Pendente (Validação do RF-01 e RF-02).
  3. Clique em Concluir. A descrição fica riscada e o badge muda para verde (Concluída) (Validação do RF-05).
  4. Abra o YARC e faça um GET http://localhost:8080/api/tarefas. A tarefa cadastrada pela interface Thymeleaf aparece no JSON da API, comprovando que ambas compartilham a mesma base de dados H2!

🌳 7. Estrutura Completa do Projeto Consolidado

Ao concluir todos os passos, a sua pasta api-spring deve estar organizada exatamente desta forma:

api-spring/
├── pom.xml                                           ← Configurações e dependências Maven
├── mvnw                                              ← Script executável do Maven para Linux/macOS
├── mvnw.cmd                                          ← Script executável do Maven para Windows
├── .mvn/
│   └── wrapper/
│       └── maven-wrapper.properties
└── src/
    ├── main/
    │   ├── java/
    │   │   └── br/com/curso/listadetarefas/api/      ← Pacote Raiz da Aplicação (Component Scan)
    │   │       ├── ListadetarefasApiApplication.java ← Ponto de entrada (método main do Spring Boot)
    │   │       └── tarefa/                           ← Subpacote com as classes do módulo de tarefas
    │   │           ├── Tarefa.java                   ← Entidade JPA (tabela relacional tb_tarefas)
    │   │           ├── TarefaRepository.java         ← Interface Spring Data JPA para acesso ao banco
    │   │           ├── TarefaService.java            ← Camada de regras de negócio e operações CRUD
    │   │           ├── TarefaRestController.java     ← Endpoints da API REST em JSON (/api/tarefas)
    │   │           └── TarefaWebController.java      ← Rotas da Interface Web Thymeleaf (/tarefas)
    │   └── resources/
    │       ├── application.properties                ← Propriedades do servidor, porta 8080 e banco H2
    │       └── templates/                            ← Pasta de templates do Thymeleaf
    │           └── tarefas.html                      ← Página web renderizada no servidor (Bootstrap 5)
    └── test/
        └── java/
            └── br/com/curso/listadetarefas/api/
                └── ListadetarefasApiApplicationTests.java ← Teste automatizado de inicialização do contexto

🎯 Checklist de Validação da Estrutura:


🚀 8. Como Executar no Laboratório

Você pode iniciar o servidor de três maneiras simples, dependendo de como prefere trabalhar:

Opção 1: Pelo VS Code (Com o Spring Boot Dashboard da VMware) — Mais Fácil

Se você instalou o Spring Boot Extension Pack da VMware:

  1. No menu lateral esquerdo do VS Code, clique no ícone da Folha Verde do Spring (Spring Boot Dashboard).
  2. Na seção Apps, você verá a sua aplicação: api-spring.
  3. Passe o mouse sobre ela e clique no botão ▶️ (Start ou Play).
  4. O terminal do VS Code abrirá automaticamente exibindo o banner do Spring Boot e os logs de inicialização. Para parar o servidor a qualquer momento, basta clicar no botão ⏹️ (Stop).

Opção 2: Pelo IntelliJ IDEA (Execução Visual Direta)

  1. Abra o arquivo ListadetarefasApiApplication.java dentro de src/main/java/br/com/curso/listadetarefas/api/.
  2. Ao lado da linha public static void main(String[] args), você verá um pequeno ícone de seta verde ▶️ na margem esquerda (gutter).
  3. Clique nele e selecione Run ‘ListadetarefasApiApplication’.
  4. O painel inferior do IntelliJ exibirá os logs do servidor.

Opção 3: Pelo Terminal (Prompt de Comando ou PowerShell)

Se preferir rodar via linha de comando:

  1. Abra o terminal na pasta exata onde está o arquivo pom.xml:
    cd catalogo_projetos/fullstack_tarefas_01_spring_api/api-spring
    
  2. Inicie a aplicação com o Maven Wrapper:
    • No Windows (PowerShell ou Prompt de Comando):
      .\mvnw.cmd spring-boot:run
      
    • No Linux ou macOS:
      ./mvnw spring-boot:run
      

Quando aparecer nos logs a mensagem Started ListadetarefasApiApplication in X.XXX seconds, o servidor estará pronto em http://localhost:8080.


🧯 9. Se der errado (Resolução de Problemas Comuns)

Sintoma Causa Mais Provável Solução
Port 8080 was already in use Outro processo ou servidor já está usando a porta 8080. No application.properties, altere para server.port=8081 e acesse via http://localhost:8081.
UnsupportedClassVersionError (class file version XX.0 vs YY.0) A versão do Java escolhida no site é mais nova que o JDK instalado na máquina. Execute java --version no terminal. No pom.xml, altere <java.version> para corresponder ao seu JDK (ex: 17 ou 21).
Database "mem:testdb" not found A URL digitada no console do H2 está incorreta. No campo JDBC URL da tela de login, certifique-se de digitar exatamente jdbc:h2:mem:testdb.
O Lombok não gera os métodos A IDE não está com o processamento de anotações habilitado. No IntelliJ IDEA: vá em Settings > Build, Execution, Deployment > Compiler > Annotation Processors e marque Enable annotation processing.
YARC retorna 415 Unsupported Media Type no POST Esqueceu de informar o cabeçalho Content-Type. No YARC, adicione o Header: Content-Type: application/json.

🌉 10. Comparativo Didático e A Ponte para a Parte 02

Agora você compreende a diferença fundamental entre as duas formas de construir para a web:

Critério Thymeleaf (Server-Side Rendering) API REST + Angular (Single Page Application)
Onde a tela é montada? No servidor Java (Spring Boot gera o HTML completo). No navegador do usuário (Angular roda em JavaScript).
O que trafega na rede? Documentos HTML inteiros a cada clique. Apenas dados brutos em JSON (muito mais leve e rápido).
Flexibilidade de Clientes Atende apenas navegadores web. Atende Web, Celular (Android/iOS), Desktop e relógios inteligentes.
Complexidade Simples, tudo no mesmo projeto. Maior, requer dois projetos separados (Backend + Frontend).

Evolução de Arquitetura: Thymeleaf (SSR) vs. Angular (SPA)


👉 Próximo Passo: Parte 02 — Lista de Tarefas com Spring Boot + Svelte (JavaScript)

Na Parte 02, daremos o próximo salto evolutivo: conectaremos uma SPA reativa e ultrarrápida em Svelte (JavaScript puro) consumindo exatamente a API REST que você acabou de criar!