A Saga do Ingresso Perfeito: Parte 3 (O Desafio da Consistência)
O Mistério da Transação Perdida
Como garantir que o Lucas não fique sem o dinheiro E sem o ingresso no mundo distribuído.
No episódio anterior, vimos que para salvar a bilheteria virtual do colapso, a equipe de engenharia quebrou o monolito em três microserviços independentes e especialistas: Ingressos (Tickets), Pagamentos e Notificações. Ao adotar a comunicação assíncrona, Lucas não precisa mais ficar travado esperando o processamento do banco; ele recebe um aviso de "Processando..." instantaneamente e pode continuar navegando.
Mas agora, surge o maior pesadelo de qualquer pessoa que desenvolve sistemas distribuídos: como garantir a consistência dos dados quando tudo está separado?
O Cenário de Horror: O que acontece quando o sistema falha?
Imagine a seguinte sequência de eventos no celular do Lucas:
- Lucas clica em "Comprar".
O Serviço de Ingressos reserva temporariamente o assento "A-12" no seu próprio banco de dados e envia uma mensagem assíncrona dizendo: ReservaCriada.
- O Serviço de Pagamentos recebe a mensagem e tenta cobrar o cartão do Lucas.
- Tragédia: O cartão do Lucas é recusado por falta de limite, ou a conexão com a operadora de cartão cai no meio do caminho.
Se estivéssemos em um monolito antigo, resolver isso seria incrivelmente simples. O monolito usaria uma transação tradicional ACID (Atomicidade, Consistência, Isolamento e Durabilidade) no banco de dados centralizado. O código executaria algo como:
BEGIN TRANSACTION;
-- Reserva o ingresso
-- Cobra o cartão
COMMIT; -- Ou aborta tudo com ROLLBACK se a cobrança falhar!
Com o ROLLBACK, se o pagamento falhasse, o banco desfazia a reserva do ingresso automaticamente. Tudo ou nada. É o poder da Atomicidade.
Mas no mundo dos microserviços, nós seguimos a regra de ouro do Shared-Nothing (Banco de Dados Isolado): cada serviço possui o seu próprio banco de dados físico e independente. O banco de dados de Ingressos não sabe que o banco de dados de Pagamentos existe, e vice-versa.

Figura 4: Arquitetura de bancos de dados isolados por serviço (Shared-Nothing).
Não existe um botão de ROLLBACK mágico que consiga cruzar a rede e desfazer uma linha escrita no banco de outro serviço. Se o pagamento do Lucas falhar, como avisamos o Serviço de Ingressos para liberar o assento "A-12" para outros fãs comprarem?
Por que não usar Transações Distribuídas (2PC)?
Alguns iniciantes sugerem usar um protocolo chamado Two-Phase Commit (2PC) ou transação distribuída, onde um coordenador central pergunta a todos os bancos se eles podem confirmar a escrita antes de finalizá-la.
Embora funcione na teoria, na prática o 2PC é um enorme gargalo. Ele bloqueia as tabelas dos bancos de dados através da rede enquanto espera a resposta de todos. Se o Serviço de Pagamento demorar 5 segundos para responder, o Serviço de Ingressos ficará travado, incapaz de vender outros ingressos. Isso destrói a escalabilidade e a alta disponibilidade que tanto lutamos para construir.
A Solução: Eventual Consistency e o modelo BASE
Para escalar de verdade, os sistemas distribuídos modernos desistem da consistência imediata e abraçam a Consistência Eventual.
Em vez de seguir o modelo rígido ACID, seguimos o modelo BASE:
- Basically Available (Basicamente Disponível): O sistema prioriza estar sempre online para receber requisições, mesmo que algumas partes estejam temporariamente desatualizadas.
- Soft-State (Estado Fluido): Os dados podem mudar ao longo do tempo sem a intervenção direta do usuário, à medida que as mensagens são entregues.
- Eventual Consistency (Consistência Eventual): Se nenhuma nova atualização for feita, eventualmente todos os bancos de dados do sistema chegarão ao mesmo estado de acordo.
Isso significa que, durante alguns segundos, o sistema do Lucas estará em um estado inconsistente: o ingresso dele está "Reservado", mas o pagamento ainda não foi confirmado. Para o negócio de shows, essa pequena janela de tempo em que as coisas estão desalinhadas é um preço aceitável a se pagar para que o site aguente milhões de acessos sem cair.
Como resolver o erro? Rolando para Frente!
Se não podemos dar um "desfazer" (rollback) físico no banco de dados, como corrigimos o problema do pagamento recusado do Lucas?
Nós usamos uma estratégia chamada Roll-Forward (Rolar para Frente): em vez de tentar voltar no tempo, nós criamos uma nova transação que corrige o estado do sistema. Se o pagamento falhar, nós publicamos um novo evento de falha na rede. O Serviço de Ingressos escuta esse evento e, de forma proativa, executa uma ação para cancelar a reserva e liberar a cadeira.
Essa dança coordenada de eventos e reações para garantir a consistência eventual tem um nome muito famoso na arquitetura de software: o Padrão SAGA.
No próximo episódio, veremos como desenhar a coreografia perfeita dessa SAGA para garantir que o Lucas consiga seu ingresso de forma segura — ou seja avisado do erro sem bagunçar os bancos de dados do sistema.




