Serviço financeiro que aplica apostas, vitórias, derrotas, reembolsos e estornos nas carteiras dos jogadores, com saldo e extrato corretos mesmo quando as mensagens chegam duplicadas, fora de ordem ou ao mesmo tempo.
Provedores de jogos enviam transações de aposta (BET, WIN, LOSS, REFUND, ROLLBACK) por HTTP ou por fila SQS. O serviço precisa aplicar cada uma na carteira certa e manter um extrato imutável, sem nunca debitar duas vezes, criar dinheiro do nada ou deixar o saldo negativo, mesmo com várias instâncias processando ao mesmo tempo. Como extensão, a carteira também aceita depósito e saque pelo caixa, com as mesmas garantias.
Solução implementada
Um único serviço NestJS. A rota HTTP e o consumer da fila chamam o mesmo use case; tudo o que muda dinheiro acontece em uma transação SQL, e só a outbox publica eventos.
Validação pura, sem abrir transação: dinheiro com exatamente 2 casas, tipo da transação e hash do payload.
Busca pela Idempotency-Key: mesmo conteúdo devolve o resultado original; conteúdo diferente é 409.
SELECT … FOR UPDATE só na linha da carteira; carteiras diferentes seguem em paralelo.
Regras de negócio no domínio, com desfecho PROCESSED, REJECTED ou PENDING_REFERENCE.
Na mesma transação: transação, novo saldo, lançamento do extrato, eventos na outbox e, se veio da fila, a inbox. Só depois do commit vem a resposta ou o ack.
Decisões de engenharia
Dinheiro nunca é number: centavos em bigint, strings com exatamente 2 casas, numeric(19,2) no banco.
Concorrência por carteira, sem lock global:SELECT … FOR UPDATE mais UPDATE condicionado pela version; lock_timeout de 5 s vira 503 com Retry-After.
Idempotência persistente:UNIQUE (idempotency_key) e SHA-256 do JSON canônico dos campos de negócio.
Uma regra de negócio, três entradas: HTTP, consumer SQS e worker de pendências usam o mesmo decide().
Inbox e outbox na transação do dinheiro: a inbox deduplica entregas com ON CONFLICT DO NOTHING, e o ack só sai depois do commit; a outbox é publicada com FOR UPDATE SKIP LOCKED.
Desafios técnicos
Mensagens fora de ordem: um estorno que chega antes da aposta fica PENDING_REFERENCE e é reprocessado com backoff exponencial até a referência existir.
Corrida real entre instâncias: os testes sobem três processos do serviço disputando a mesma carteira e matam um deles com SIGKILL entre o commit e o ack.
Invariantes no banco: constraints e triggers garantem as regras mesmo se o código falhar.
Resultados
248 testes automatizados passando, rodando contra PostgreSQL e LocalStack reais, sem mocks. A mesma aposta enviada 50 vezes em paralelo resulta em um único débito.
Arquitetura
Fluxo de uma transação
HTTP e SQS convergem no mesmo use case; tudo que muda dinheiro acontece numa transação SQL.
clique em um componente · arraste para mover · scroll para zoom
passo a passo
etapa 0/5
Tecnologias utilizadas
Backend
BunRuntime do serviço e dos scripts
NestJSAPI HTTP, consumer SQS e workers
TypeScriptModo estrito em todo o código
MIMikroORMUnit of work e lock pessimista por carteira
APIs RESTPOST /wagering/transactions com Idempotency-Key
Dados & Mensageria
PostgreSQLCarteiras, extrato, inbox e outbox; constraints e triggers como última barreira
AWAWS SQSFila FIFO de entrada, DLQ e fila de eventos (LocalStack)
Cloud & DevOps
DockerCompose com PostgreSQL, LocalStack e backend
Principais funcionalidades
Idempotência persistenteIdempotency-Key com hash do payload
Lock por carteiraSem lock global; espera máxima de 5 s
Ledger append-onlyCada lançamento guarda saldo antes e depois
Transactional outboxEventos publicados com SKIP LOCKED
Imagens e demonstrações
Wallet Console: apostar, ganhar, perder e acompanhar cada requisição
Extrato append-only e conferência de saldo × extrato
Flow Explorer: duas apostas disputando o lock da carteira
Mesma key com outro conteúdo: 409 e nada gravado
Temas claro e escuro
Sobre o projeto
Desafio técnico da Jungle Gaming. Além do backend, o projeto traz dois frontends: o Wallet Console, para usar o sistema como um operador de cassino, e o Flow Explorer, que executa uma rota e anima o caminho que ela fez pelo código, com o SQL, os locks e a fila.
Do planejamento ao código, com o time inteiro na mesma tarefa
Plataforma para times de software que reúne quadro, chat, documentos e um editor de código. Cada tarefa vira um canvas infinito com a especificação, o código e o time, e o contexto não se perde entre ferramentas.
Plataforma de transparência parlamentar: o que cada deputado federal propôs e como votou, direto dos dados abertos da Câmara, com link para conferir cada informação na fonte.