Pular para o conteúdo principal

Compra com cartão

Autorização síncrona, e um único evento consumido de forma independente por antifraude, notificação, cashback e analytics.

Este estudo de caso mostra o padrão mais comum de arquitetura orientada a eventos em sistemas de pagamento: uma decisão síncrona e crítica (autorizar ou não a compra) seguida de um evento único que múltiplos sistemas consomem de forma completamente independente, cada um preocupado com uma responsabilidade diferente.

O evento de domínio

A autorização em si — aprovar ou negar a compra no momento em que o cartão é passado — não é feita via Kafka. É uma chamada síncrona, com resposta em milissegundos, entre a maquininha (ou gateway de pagamento) e o autorizacao-service, que decide com base em saldo/limite disponível. Isso é intencional: o Capítulo 1 já explicou que comunicação request/response com resposta imediata não é o caso de uso do Kafka.

O que acontece depois da decisão síncrona é onde o Kafka entra: assim que a compra é aprovada, o autorizacao-service publica um evento CompraAutorizada — um fato que já aconteceu — e segue em frente, sem saber (nem precisar saber) quem vai reagir a isso.

{
  "eventId": "3a91f7c2-...",
  "cartaoId": "card_88213",
  "clienteId": "cli_50213",
  "valor": 15990,
  "estabelecimento": "Loja XYZ",
  "categoria": "varejo",
  "autorizadoEm": "2026-07-31T18:44:02Z"
}

Topologia de tópicos

Tópico e configuração

ItemDecisão
Tópicocompras.autorizadas
KeycartaoId
Partitions18
Replication factor3
Retention15 dias

Por que a key é cartaoId, não clienteId

Dica de entrevista

A escolha entre cartaoId e clienteId como key é um ótimo teste de entendimento de ordenação (Capítulo 4). Se um cliente tem múltiplos cartões e o requisito é apenas "processar compras do mesmo cartão em ordem" (ex.: para detectar uma sequência suspeita de compras no mesmo cartão em curto intervalo), a key correta é cartaoId. Se o requisito fosse consolidar comportamento do cliente como um todo, independente do cartão usado, clienteId seria a escolha — trocando ordenação por cartão por ordenação por cliente. Não existe resposta certa universal; depende de qual relação de ordem o domínio realmente precisa.

Consumidores independentes

ProducerTopic: pagamentosPartition 0Partition 1Partition 2Consumer Group: notificacao-serviceConsumer 0Consumer 1Consumer 2
Múltiplos Consumer Groups leem o mesmo tópico compras.autorizadas de forma independente.
  • antifraude-service: avalia a compra contra regras de risco (padrão de gasto, geolocalização, velocidade entre transações). Pode, em casos suspeitos, publicar um evento separado CompraSuspeita para acionar um bloqueio preventivo do cartão.
  • notificacao-service: envia push "Compra de R$ 159,90 aprovada em Loja XYZ".
  • cashback-service: calcula e credita o cashback aplicável, com base na categoria do estabelecimento.
  • analytics-service: alimenta um data warehouse para dashboards de gastos e modelos de recomendação.

Nenhum desses quatro consumers sabe da existência dos outros três. Uma lentidão no analytics-service (por exemplo, durante uma carga pesada de reprocessamento de relatórios) não atrasa a notificação push, que precisa ser quase instantânea para uma boa experiência do cliente.

Idempotência: nem todo consumer precisa do mesmo rigor

O nível de proteção contra duplicidade varia por consumer

Os quatro consumers deste evento têm exigências de idempotência muito diferentes entre si — um erro comum é aplicar o mesmo padrão de proteção (tabela de eventos processados) em todos, quando alguns já são naturalmente tolerantes à duplicidade.

Exigência de idempotência por consumer

ConsumerEfeito de duplicidadeProteção necessária
cashback-serviceCashback creditado duas vezes — perda financeira diretaeventId + constraint de unicidade, obrigatório
notificacao-serviceCliente recebe duas notificações — irritante, não críticoNenhuma proteção especial necessária
antifraude-serviceReavalia a mesma compra duas vezes — trabalho redundante, não incorretoOpcional, por eficiência, não por correção
analytics-serviceCompra contada duas vezes na agregação — métricas infladasDepende da consulta: agregações via upsert por eventId resolvem naturalmente

Retry e DLQ

O cashback-service, por lidar com valor financeiro direto, usa retry com backoff e DLQ (Capítulo 9): se a regra de cálculo de cashback falhar (ex.: tabela de categorias indisponível), a mensagem é reprocessada até 3 vezes antes de ir para compras.autorizadas.cashback.dlq, sem bloquear o crédito de cashback das compras seguintes.

Replay

Se a tabela de categorias de cashback for corrigida retroativamente (uma categoria que deveria dar 2% e estava configurada com 1%), o cashback-service pode fazer replay dos últimos 15 dias (dentro da retenção) para recalcular e creditar a diferença — desde que a lógica de crédito seja um upsert por eventId, evitando duplicar o cashback já creditado corretamente.

Observabilidade

O que cada time monitora

TimeMétrica prioritária
CashbackConsumer lag e taxa de erro — atraso aqui vira reclamação de cliente sobre cashback não creditado
NotificaçãoLatência producer → consumer — o push precisa chegar em segundos para ter valor
AntifraudeTaxa de mensagens na DLQ — falhas de avaliação de risco não podem passar despercebidas
AnalyticsConsumer lag agregado ao longo do dia — atraso aqui é tolerável em minutos, não segundos

Resumo

Este caso ilustra a diferença entre comunicação síncrona (a autorização em si, fora do Kafka) e assíncrona (tudo que reage à compra já aprovada). Um único evento, CompraAutorizada, é consumido por quatro sistemas com necessidades completamente diferentes de latência, idempotência e tolerância a falha — cada um configurado de acordo com sua própria criticidade, não com um padrão único aplicado indiscriminadamente a todos.