📚 Pré-requisitos Teóricos: este projeto aplica conceitos ensinados em Matriz de 20 Capítulos de Aplicações Full Stack (este módulo). Recomendado revisar antes de começar.
v2.0`
Neste projeto, construiremos um sistema completo de “Lista de Tarefas” (To-Do List), demonstrando como diferentes aplicações cliente podem consumir uma única fonte de dados central (API). A arquitetura final será:
flowchart TD
subgraph "🎛️ Gerenciamento e Automação"
Script["🛠️ Painel de Controle (listadetarefas-painel.ps1)"]
end
subgraph "📱 Clientes (Frontends)"
Web["💻 Frontend Web (Angular)"]
Desktop["🖥️ Frontend Desktop (JavaFX)"]
Mobile["📱 Frontend Mobile (Android)"]
end
subgraph "⚙️ Serviços (Backend)"
API["🔌 Backend API (Spring Boot)"]
DB["🗄️ Banco de Dados Em Memória H2"]
end
%% Conexões de Dados
Web -->|Requisições HTTP/JSON| API
Desktop -->|Requisições HTTP/JSON| API
Mobile -->|Requisições HTTP/JSON| API
API --- DB
%% Conexões de Gerenciamento
Script -- Gerencia --> API
Script -- Gerencia --> Web
Script -- Gerencia --> Desktop
Script -- Gerencia --> Mobile
—–`
listadetarefas-api)Objetivo: Criar o serviço central que irá gerenciar os dados das tarefas, servindo como a única fonte de verdade para todos os clientes.
MavenJavabr.com.cursolistadetarefas-apibr.com.curso.listadetarefas.apiDependencies):
Spring Web, Spring Data JPA, H2 Database, Lombok.Após criar o projeto, sua estrutura de pastas principal será:
listadetarefas-api/
├── src/
│ └── main/
│ ├── java/
│ │ └── br/com/curso/listadetarefas/api/
│ │ └── ListadetarefasApiApplication.java
│ └── resources/
│ └── application.properties
└── pom.xml
Abra o arquivo src/main/resources/application.properties e substitua seu conteúdo por este:
# Permite que o servidor aceite conexões de qualquer endereço de rede da máquina.
server.address=0.0.0.0
# Habilita o console web do H2
spring.h2.console.enabled=true
# Define o caminho para acessar o console
spring.h2.console.path=/h2-console
# Configurações do Datasource para H2
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
Vamos definir a estrutura da nossa tabela de tarefas.
erDiagram
TB_TAREFAS {
long id PK "Auto-incremento"
string descricao
boolean concluida
}
src/main/java/br/com/curso/listadetarefas/api, crie um novo pacote chamado tarefa.tarefa, crie a classe Tarefa.java.// src/main/java/br/com/curso/listadetarefas/api/tarefa/Tarefa.java
package br.com.curso.listadetarefas.api.tarefa;
import jakarta.persistence.*;
import lombok.Data;
@Data
@Entity
@Table(name = "tb_tarefas")
public class Tarefa {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String descricao;
private boolean concluida;
}
api/
└── src/main/java/br/com/curso/listadetarefas/api/
├── tarefa/
│ └── Tarefa.java # <- Arquivo criado
└── ListadetarefasApiApplication.java
Agora, criaremos as classes que formam a arquitetura da nossa API: Repository (acesso a dados), Service (regras de negócio) e Controller (endpoints HTTP).
classDiagram
TarefaController ..> TarefaService : Usa
TarefaService ..> TarefaRepository : Usa
TarefaRepository ..> Tarefa : Gerencia
class TarefaController {
+List~Tarefa~ listarTarefas()
+Tarefa criarTarefa(Tarefa tarefa)
+ResponseEntity~Tarefa~ atualizarTarefa(Long id, Tarefa tarefa)
+ResponseEntity~Void~ deletarTarefa(Long id)
}
class TarefaService {
+List~Tarefa~ listarTodas()
+Tarefa criar(Tarefa tarefa)
+Tarefa atualizar(Long id, Tarefa tarefa)
+void deletar(Long id)
}
class TarefaRepository {
<<interface>>
}
class Tarefa {
-Long id
-String descricao
-boolean concluida
}
tarefa, crie as seguintes classes e interfaces:TarefaRepository.java
package br.com.curso.listadetarefas.api.tarefa;
import org.springframework.data.jpa.repository.JpaRepository;
public interface TarefaRepository extends JpaRepository<Tarefa, Long> {
}
TarefaService.java
package br.com.curso.listadetarefas.api.tarefa;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class TarefaService {
@Autowired
private TarefaRepository tarefaRepository;
public List<Tarefa> listarTodas() { return tarefaRepository.findAll(); }
public Tarefa criar(Tarefa tarefa) { return tarefaRepository.save(tarefa); }
public Tarefa atualizar(Long id, Tarefa tarefaAtualizada) {
return tarefaRepository.findById(id)
.map(tarefaExistente -> {
tarefaExistente.setDescricao(tarefaAtualizada.getDescricao());
tarefaExistente.setConcluida(tarefaAtualizada.isConcluida());
return tarefaRepository.save(tarefaExistente);
}).orElseThrow(() -> new RuntimeException("Tarefa não encontrada com o id: " + id));
}
public void deletar(Long id) {
if (!tarefaRepository.existsById(id)) {
throw new RuntimeException("Tarefa não encontrada com o id: " + id);
}
tarefaRepository.deleteById(id);
}
}
TarefaController.java
package br.com.curso.listadetarefas.api.tarefa;
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 tarefaService;
@GetMapping
public List<Tarefa> listarTarefas() { return tarefaService.listarTodas(); }
@PostMapping
public Tarefa criarTarefa(@RequestBody Tarefa tarefa) { return tarefaService.criar(tarefa); }
@PutMapping("/{id}")
public ResponseEntity<Tarefa> atualizarTarefa(@PathVariable Long id, @RequestBody Tarefa tarefa) {
try {
Tarefa atualizada = tarefaService.atualizar(id, tarefa);
return ResponseEntity.ok(atualizada);
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deletarTarefa(@PathVariable Long id) {
try {
tarefaService.deletar(id);
return ResponseEntity.noContent().build();
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
}
api/
└── src/main/java/br/com/curso/listadetarefas/api/
├── tarefa/
│ ├── Tarefa.java
│ ├── TarefaController.java
│ ├── TarefaRepository.java
│ └── TarefaService.java
└── ListadetarefasApiApplication.java
flowchart LR
Usuario("👤 Usuário (via Cliente)")
subgraph "Sistema"
UC1("Listar todas as tarefas")
UC2("Adicionar nova tarefa")
UC3("Atualizar uma tarefa")
UC4("Deletar uma tarefa")
end
Usuario --> UC1
Usuario --> UC2
Usuario --> UC3
Usuario --> UC4
Execute a Aplicação:
ListadetarefasApiApplication.java../mvnw spring-boot:runTeste com Cliente REST (ex: Postman):
http://localhost:8080/api/tarefas e verifique todas as operações de CRUD (GET, POST, PUT, DELETE) como detalhado no guia anterior.Teste com o Console H2:
http://localhost:8080/h2-console no navegador.jdbc:h2:mem:testdbsaSELECT * FROM TB_TAREFAS; para ver os dados diretamente no banco.—–`
listadetarefas-web)Objetivo: Criar uma interface web moderna e reativa para interagir com a API.
npm install -g @angular/cli)ng new listadetarefas-web --style=css
cd listadetarefas-web.ng generate interface models/tarefa
ng generate service services/tarefa
ng generate component components/task-list
listadetarefas-web/
└── src/
└── app/
├── components/
│ └── task-list/
│ ├── task-list.component.css
│ ├── task-list.component.html
│ └── task-list.component.ts
├── models/
│ └── tarefa.ts
└── services/
└── tarefa.service.ts
Siga os passos e use os códigos fornecidos no guia anterior para os seguintes arquivos:
src/app/models/tarefa.tssrc/app/services/tarefa.service.tssrc/app/components/task-list/task-list.component.tssrc/app/components/task-list/task-list.component.htmlsrc/app/components/task-list/task-list.component.css (use a versão melhorada e com variáveis).Para corrigir o erro 'app-task-list' is not a known element', integre o componente principal:
provideHttpClient(withFetch()) está em src/app/app.config.ts.src/app/app.component.ts para importar e usar o TaskListComponent:// src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { TaskListComponent } from './components/task-list/task-list.component';
@Component({
selector: 'app-root',
imports: [ RouterOutlet, TaskListComponent ], // Importado aqui
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent { }
src/app/app.component.html e adicione apenas:<app-task-list></app-task-list>
listadetarefas-web), rode ng serve --open.—`
No seu editor/IDE, abra a pasta deste projeto (File > Open Folder) ou navegue via terminal:
cd fullstack_tarefas_02_spring_js
./mvnw spring-boot:run
# No frontend: npm install && npm run dev
[!TIP] Dica para execução a partir da raiz do repositório: Se você abriu o repositório completo no VS Code, basta navegar até a pasta antes de executar: cd proj_aplicacoes_full_stack/projetos/fullstack_tarefas_02_spring_js