Plataforma de ingestão e categorização de extratos bancários — projeto de estudo focado em Java 21 + Spring Boot 3 + AWS, com arquitetura hexagonal e processamento assíncrono via SQS.
Construído para servir como referência prática para entrevistas em vagas que exigem Java backend e AWS. Não é "olá mundo".
┌─────────────┐ ┌──────────┐ ┌─────────┐ ┌─────────────┐
│ POST │ ──▶ │ API │ ──▶ │ S3 │ │ Postgres │
│ /extratos │ │ Spring │ │ (upload)│ │ (transac.) │
└─────────────┘ │ Boot │ ──▶ │ SQS │ └─────────────┘
└──────────┘ │ (queue) │ ▲
└────┬────┘ │
▼ │
┌─────────┐ │
│ Worker │ ───────────┤
│ (parse, │ │
│categori │ ┌─────────────┐
│ za) │ ─▶│ DynamoDB │
└────┬────┘ │ (agregados) │
▼ └─────────────┘
┌─────────┐
│ SNS │ ─▶ email / webhook
│ (event) │
└─────────┘
Estilo: Hexagonal (ports & adapters).
domain/— modelo puro, sem dependência de framework.application/port/in— use cases (entrada).application/port/out— contratos de infra (saída).application/usecase— implementação dos use cases.infrastructure/adapter/in/web— REST controllers.infrastructure/adapter/in/messaging— listener SQS.infrastructure/adapter/out/persistence— JPA / Postgres.infrastructure/adapter/out/aws— S3, SQS, SNS, DynamoDB.
| Camada | Tecnologia |
|---|---|
| Linguagem | Java 21 (LTS, com records, switch patterns, virtual threads) |
| Framework | Spring Boot 3.3 |
| AWS | Spring Cloud AWS 3.2 + AWS SDK v2 |
| Persistência | PostgreSQL 16 + Spring Data JPA + Flyway |
| NoSQL | DynamoDB (agregados mensais por categoria) |
| Mensageria | SQS (com DLQ + redrive policy) e SNS |
| Storage | S3 com pre-signed URLs (cliente faz upload direto, sem passar pelo backend) |
| Build | Maven |
| Testes | JUnit 5 + AssertJ + Testcontainers (Postgres + LocalStack) |
| Docs | SpringDoc OpenAPI (Swagger UI em /swagger-ui.html) |
| Infra local | Docker Compose + LocalStack |
- Pre-signed URL no upload: o cliente envia o arquivo direto pro S3, o backend não vira gargalo nem carrega o arquivo em memória. Padrão usado em produção.
- Idempotência: o
hashda combinaçãocontaId + período + nomeArquivoevita reprocessar o mesmo extrato. Re-tentativas do SQS são seguras. - DLQ (Dead Letter Queue) configurada com
maxReceiveCount = 3— mensagens com falha repetida saem da fila principal e não bloqueiam outras. - Hexagonal: trocar Postgres por outro banco, ou SQS por Kafka, exige escrever um novo adapter — o domínio não muda. Mostra design responsável.
@Transactionalapenas no use case — adapter de persistência não declara transação.- Records do Java 21 para DTOs/eventos/value objects imutáveis.
- Sem Lombok: o projeto inteiro roda só com a linguagem (records + switch patterns + getters explícitos onde precisa). Demonstra que conhece o Java moderno.
- Java 21 (recomendo via SDKMAN:
sdk install java 21.0.11-tem) - Maven 3.9+ (
sdk install maven) - Docker
docker compose up -dSobe:
- Postgres em
localhost:5436 - LocalStack (S3 + SQS + SNS + DynamoDB) em
localhost:4566 - Script de inicialização cria bucket, filas, tópico e tabela DynamoDB automaticamente.
mvn spring-boot:runA aplicação sobe em http://localhost:8090 no profile local (lê do LocalStack).
- Swagger UI: http://localhost:8090/swagger-ui.html
- Actuator health: http://localhost:8090/actuator/health
# 3.1 Pedir URL de upload
RESPONSE=$(curl -s -X POST http://localhost:8090/api/v1/extratos \
-H 'Content-Type: application/json' \
-d '{
"contaId": "11111111-1111-1111-1111-111111111111",
"periodoInicio": "2026-04-01",
"periodoFim": "2026-04-30",
"nomeArquivo": "abril-2026.csv"
}')
echo $RESPONSE
EXTRATO_ID=$(echo $RESPONSE | jq -r .extratoId)
UPLOAD_URL=$(echo $RESPONSE | jq -r .uploadUrl)
# 3.2 Subir o CSV no S3 via pre-signed URL
curl -X PUT --data-binary @exemplos/extrato-exemplo.csv \
-H 'Content-Type: text/csv' \
"$UPLOAD_URL"
# 3.3 Disparar processamento (manual por enquanto — em prod seria S3 Event Notification)
# A mensagem já foi enviada na SQS pelo passo 3.1 — o worker já está processando.
# 3.4 Consultar
sleep 2
curl -s http://localhost:8090/api/v1/extratos/$EXTRATO_ID | jq# Rodar testes
mvn test
# Inspecionar recursos do LocalStack
aws --endpoint-url=http://localhost:4566 s3 ls
aws --endpoint-url=http://localhost:4566 sqs list-queues
aws --endpoint-url=http://localhost:4566 sns list-topics
aws --endpoint-url=http://localhost:4566 dynamodb scan --table-name ledger-agregados
# Acessar Postgres
psql -h localhost -p 5436 -U ledger -d ledger| Etapa | O que adiciona | Conceito AWS / Java |
|---|---|---|
| 1 (atual) | Upload + processamento async + categorização | S3 pre-signed, SQS, SNS, DynamoDB, Flyway |
| 2 | S3 Event Notification ⇒ SQS automático (sem disparo manual) | EventBridge / S3 → SQS |
| 3 | Testes de integração com Testcontainers + LocalStack | TestContainers, perfis de teste |
| 4 | Resiliência: retry, circuit breaker, idempotência forte | Resilience4j, Outbox Pattern |
| 5 | Observabilidade: métricas Micrometer + traces OpenTelemetry | CloudWatch, X-Ray |
| 6 | Autenticação JWT + Spring Security | Cognito ou JWT próprio |
| 7 | Deploy: Docker image + ECS Fargate via Terraform | IaC, ECS, ECR, IAM |
| 8 | Lambda Java 21 (SnapStart) para processamento | AWS Lambda + Java |
- S3 — armazenamento de objetos. Padrão para arquivos. Pre-signed URLs são a forma certa de receber upload sem expor backend.
- SQS — fila gerenciada. Desacopla a ingestão do processamento, permite retry automático e DLQ.
- SNS — pub/sub. Fan-out: um evento "extrato processado" pode notificar múltiplos consumidores (email, BI, sistema legado) sem o produtor saber quem está ouvindo.
- DynamoDB — NoSQL serverless. Bom para agregados pré-calculados com chave conhecida (consulta
contaId + anoMesé O(1)).