EN

Nº 06de 22Destaque

E-commerce transacional (sob NDA)

E-commerce transacional em Spring Boot: outbox com retry exponencial, idempotência de webhook e rate limit em Redis com circuit breaker

Ano
2026
Status
EM CURSO
Papel
Solo · sob contrato
Stack
Java 21 · Spring Boot · PostgreSQL · Redis +5
commerce-nda
EC

Java 21

Sem captura pública — capa gerada

01O projeto

Loja própria de uma marca brasileira — nome sob contrato — em monolito modular Java 21 / Spring Boot 4.1 com storefront Next.js. O sistema move dinheiro, então a engenharia é quase toda sobre o caminho infeliz: webhook de pagamento validado por assinatura e deduplicado por tabela de webhooks processados; e-mails transacionais em outbox, com claim numa transação curta e envio fora de lock; retentativa com backoff exponencial e chave de idempotência no provedor; rate limit por chave em Redis via script Lua atômico que falha aberto quando o Redis cai. 12 módulos de domínio, migrações Flyway e testes de integração com Testcontainers em Postgres e Redis reais.

02O que foi feito

  1. Outbox transacional: claim em transação curta, envio sem segurar lock, backoff exponencial de 30s dobrando até o teto de 1h com jitter

  2. Idempotência ponta a ponta: webhook processado uma única vez, Idempotency-Key no provedor de e-mail

  3. Rate limit por chave em Redis com script Lua atômico, que falha aberto e tem circuit breaker com sonda em half-open

  4. Webhook de pagamento validado por assinatura HMAC; webhook de NF-e por token secreto próprio em comparação constant-time — ambos fail-closed

  5. 12 módulos de domínio, Flyway e testes de integração com Testcontainers (Postgres + Redis reais)

03Decisões

Decisão 01

Pergunta: Por que outbox e não chamar o provedor de e-mail dentro da transação?

Resposta: Chamada HTTP dentro de transação segura conexão do pool enquanto espera a rede. O outbox quebra em três passos: transação curta que faz o claim, envio sem lock nenhum, transação curta que grava o resultado. A Idempotency-Key é o id da linha, então reenvio depois de crash é no-op.

Decisão 02

Pergunta: Por que o rate limiter falha ABERTO?

Resposta: Se o Redis cair, bloquear todo o tráfego derruba o checkout — o custo de deixar passar tráfego por alguns minutos é menor que o de parar de vender. Ele loga ERROR para alertar, e o circuit breaker evita que cada request pague o timeout de conexão enquanto o Redis está fora.

Decisão 03

Pergunta: Por que monolito modular e não microsserviços?

Resposta: Projeto solo, ~100 pedidos/mês. Microsserviço aqui compraria latência de rede e complexidade de deploy sem resolver nenhum problema que eu tenha. Os módulos têm fronteira clara para o dia em que valer a pena separar.

04Stack

  • Java 21
  • Spring Boot
  • PostgreSQL
  • Redis
  • Next.js
  • TypeScript
  • Testcontainers
  • Flyway
  • Docker
Próximo projeto →Nº 07

Monitoramento em tempo real: métricas do backend FastAPI transmitidas por WebSocket e renderizadas em 3D