API REST de estoque e pedidos construída para demonstrar backend de produção: regras de negócio, concorrência, segurança, banco relacional, documentação e observabilidade.
Projeto de portfólio de Ronael Moura / Ronas Tech. Os dados de demonstração são fictícios.
Não é apenas um CRUD. O StockFlow trata problemas comuns de sistemas reais:
- reserva de estoque dentro de transações MySQL;
- proteção contra pedidos duplicados com
Idempotency-Key; - concorrência otimista em movimentações de estoque;
- máquina de estados para impedir transições inválidas;
- ledger imutável de movimentações e trilha de auditoria;
- Outbox Pattern para eventos confiáveis;
- autenticação com access token curto e rotação de refresh token;
- autorização por papéis:
ADMIN,MANAGER,OPERATOReVIEWER; - documentação OpenAPI interativa com Scalar;
- logs estruturados e rastreamento por
x-request-id.
Node.js 22, TypeScript, Express 5, MySQL 8, Zod, JWT, bcrypt, Pino, Scalar, Vitest e Docker.
flowchart LR
A[Pedido em rascunho] -->|Confirmar| B[Reserva transacional]
B -->|Estoque disponível| C[Pedido confirmado]
B -->|Saldo insuficiente| D[409 sem alteração]
C -->|Expedir| E[Baixa física + evento]
C -->|Cancelar| F[Liberação da reserva]
Cada mudança crítica grava, na mesma transação:
- o novo estado do pedido ou estoque;
- o movimento de inventário;
- o evento de auditoria;
- o evento pendente da Outbox.
Mais detalhes em Decisões de arquitetura.
cp .env.example .env
docker compose up --buildAcesse:
- API:
http://localhost:3333 - documentação:
http://localhost:3333/docs - especificação:
http://localhost:3333/openapi.json
Credenciais locais do seed:
admin@stockflow.dev
StockFlow@2026
Com um MySQL 8 disponível:
npm install
cp .env.example .env
npm run db:migrate
npm run db:seed
npm run dev| Script | Objetivo |
|---|---|
npm run quality |
Executa tipos, lint, testes e validação OpenAPI |
npm run demo:scenario |
Cria, confirma e expede um pedido pela API |
npm run routes:audit |
Exibe o catálogo público de operações |
npm run openapi:validate |
Valida o contrato e detecta rotas não documentadas |
npm run outbox:drain |
Processa eventos transacionais pendentes |
npm run db:reset |
Recria apenas bancos cujo nome começa com stockflow |
npm run test:coverage |
Gera relatório de cobertura das regras de negócio |
curl -X POST http://localhost:3333/api/v1/orders \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Idempotency-Key: checkout-2026-0001" \
-H "Content-Type: application/json" \
-d '{
"customerName": "Ana Souza",
"customerEmail": "ana@example.com",
"items": [{
"productId": "30000000-0000-4000-8000-000000000001",
"warehouseId": "20000000-0000-4000-8000-000000000001",
"quantity": 2
}]
}'Repetir a chamada com a mesma chave retorna o recurso original em vez de criar outro pedido.
src/
├── config/ variáveis validadas
├── lib/ banco, erros, auditoria e outbox
├── middlewares/ autenticação, autorização e request context
├── modules/ auth, produtos, estoque, pedidos e dashboard
├── openapi.ts contrato da API
├── app.ts composição HTTP
└── server.ts ciclo de vida do processo
Consulte SECURITY.md. Nunca reutilize as credenciais ou a chave JWT de desenvolvimento em produção.
Desenvolvido por Ronael Moura — criador da Ronas Tech.
Contexto. Dois pedidos podem disputar o mesmo saldo disponível. Consultar o saldo e alterá-lo em operações independentes deixa espaço para decisões baseadas em dados desatualizados.
Decisão implementada. A rota de confirmação usa uma transação e consulta o estoque com SELECT ... FOR UPDATE. Calcula a disponibilidade como quantity - reserved; saldo insuficiente gera 409 INSUFFICIENT_STOCK. A reserva, o movimento, a atualização do pedido, a auditoria e a Outbox são escritos dentro da transação. A expedição é uma etapa separada da reserva.
Alternativas para comparação. Baixar o estoque físico na criação simplificaria o fluxo, mas misturaria intenção de compra com expedição. Uma leitura sem bloqueio exigiria outra estratégia de controle concorrente. Essas alternativas explicam os compromissos do desenho, não uma decisão histórica de uma equipe.
Evidência e reprodução. Consulte as decisões de arquitetura, as regras testadas e o cenário demonstrativo. Com ambiente local e banco configurados, npm run demo:scenario executa o fluxo demonstrativo. Os testes de regras não comprovam, por si só, concorrência real em MySQL; a revisão documental não executou um teste de carga.
Limite. Bloqueios têm custo de contenção. Carga concorrente, deadlocks, retentativas e comportamento do consumidor da Outbox precisam de validação própria antes de uso crítico. Não há resultados de desempenho comercial declarados neste case.