Migramos 2 milhões de contas de um core monolítico para microserviços em 18 meses
Um relato franco sobre a perda de garantias ACID, o aumento de 40% na infraestrutura e o trabalho silencioso de reconciliação para mover 2 milhões de CPFs sem quebrar o Banco Central.


Engenheiros de software costumam ter uma visão romântica da refatoração: imaginam um código limpo, testável e escalável, livrando-se da dívida técnica acumulada por anos. A realidade de migrar o coração pulsante de um banco digital, no entanto, parece mais uma cirurgia de coração aberta realizada em um trem em movimento. Quando assumimos a tarefa de mover 2 milhões de contas de um core monolítico baseado em mainframe para uma arquitetura de microserviços em nuvem, o romance acabou rápido. O que sobrou foi 18 meses de puro gerenciamento de risco.
Não é apenas sobre reescrever Java para Go ou Kotlin. O desafio é garantir que o real, aquele que não perdoa, continue conciliando enquanto você troca o motor do avião em pleno voo.
O mito do "Zero Downtime" absoluto em refatorações massivas
Há uma crença disseminada em meetup de arquitetura de que, com técnicas como Blue-Green Deployment e Canary Releases, você pode refatorar qualquer sistema sem que o usuário final perceba. Na teoria, isso é lindo. Na prática, quando você lida com um core bancário que atende às exigências do STR (Sistema de Transferência de Reservas) do Banco Central, a independência de deploy é uma ilusão perigosa.
Em nossa migração, tentamos manter a premissa de disponibilidade total durante as primeiras fases. O resultado foi um desastre de consistência temporária que quase nos custou uma notificação da fiscalização. O problema não era o código dos novos serviços em si, mas a sincronização do estado entre o legado e o novo.
A verdade dura é que, em algum momento, você precisa parar de escrever no lugar antigo para começar a escrever no novo. Negociamos com o produto uma janela de manutenção de apenas 4 horas em uma madrugada de domingo, para o corte final (cutover) do cadastro. Fomos massacrados nas redes sociais por aquele PIX que não passou, mas evitamos a corrupção de dados que teria ocorrido se tivéssemos insistido na troca "ao vivo" e sem pausa. Aceitar o downtime controlado como medida de segurança, e não como falha de engenharia, foi o primeiro passo para o sucesso.
Achar que ACID é inegociável e o SAGA é traição
Bancos nascem e crescem respirando transações ACID (Atomicidade, Consistência, Isolamento e Durabilidade). O monolito garantia que, se você debitasse de uma conta e creditasse em outra, ou as duas coisas aconteciam, ou nenhuma acontecia. Em um ambiente distribuído, isso é matematicamente impossível sem travar o sistema inteiro, o que jogaria fora a escalabilidade que buscávamos.
O erro clássico é tentar forçar consistência forte em microserviços usando protocolos de consenso pesados tipo Two-Phase Commit. Isso transforma sua arquitetura elástica em um sistema monolítico distribuído, onde o pior dos dois mundos se encontra: latência de rede e bloqueios de banco de dados.
Tivemos que engolir o pílula da consistência eventual. Implementamos o padrão SAGA com compensações para transações complexas. A dor de cabeça real não é técnica, é explicá-la para a área de compliance. "Como assim o cliente viu o saldo baixar, mas o destinatário só recebeu 200 milissegundos depois?".
Para contornar isso, criamos uma camada de reconciliação que roda a cada 5 minutos, varrendo a base em busca de transações "orfas" ou pendentes. Essa tabela de reconciliação, que no início consideramos um "debt técnico", virou o nosso ativo mais confiável. Se o SAGA falhasse no meio do caminho, um processo assíncrono garantia a integridade financeira. Aprendemos que, em 2026, a "confiabilidade" de um banco digital depende menos do banco de dados e mais da qualidade da sua fila de mensagens (Kafka, no nosso caso) e dos seus dead-letter queues.

Microserviços resolvem a lógica de negócio, mas multiplicam a superfície de ataque
Muitos arquitetos vendem a ideia de que dividir o monólito reduz a complexidade. Isso é mentira. Você troca a complexidade interna de um código gigantesco pela complexidade externa da comunicação entre dezenas de serviços.
Durante a migração, percebemos que a latência de rede, antes desprezível dentro de um mesmo processo, tornou-se um inimigo voraz. Uma chamada simples de "Consultar Saldo", que levava 20ms no monolito, passou a envolver três ou quatro "hops" de serviço (Cliente -> Conta -> Limite -> Ledger). Sem um cache distribuído eficiente, a experiência do usuário degringou.
Pior ainda foi o custo de infraestrutura. Ao sair de um grande cluster monolítico para 15 serviços distintos, nossa conta de nuvem na AWS — especificamente os custos de tráfego entre Availability Zones e o provisionamento de bancos de dados gerenciados para cada domínio — subiu cerca de 35% nos primeiros seis meses. Cada novo contexto delimitado (Bounded Context) demandava sua própria instância de PostgreSQL e seu próprio replica set. A economia de escala do monolito foi perdida, e só recuperamos o controle financeiro da nuvem depois de implementar pods de autoscaling agressivos e derrubar serviços ociosos durante a madrugada.
Por que o "Strangler Fig" foi nossa única salvação
O padrão Strangler Fig (figueira estranguladora) é clichê, mas foi a única tática que nos permitiu dormir. A ideia é ir envolvendo o legado, função por função, até que ele morra por falta de uso. Não tentamos fazer uma "Big Bang Rewrite". O erro de muitos bancos-digitais é tentar reescrever tudo do zero, o que geralmente resulta em um projeto que nunca entra em produção.
Nossa rota de migração seguiu um fluxo de dados:
- Dupla Escrita: Todo novo cadastro ou transação era escrita no MongoDB (novo) e replicada para o Oracle (antigo).
- Sombra: O sistema novo processava tudo, mas ignorávamos o resultado, usando apenas para auditar a lógica.
- Corte: Redirecionamos o tráfego de leitura para o novo core, mantendo a escrita no antigo.
- Cegueira no Legado: Paramos de ler do antigo, mas ainda escrevíamos.
- Desligamento: Apenas então desligamos a escrita no monolito.
O desafio técnico colossal aqui foi manter os ID's de transação compatíveis. Tivemos que usarSnowflake IDs (sequenciais e distribuídos) para garantir que o novo sistema não gerasse um conflito de chave primária com o legado durante a fase de dupla escrita. Se uma transação chegasse no legado com ID 1000 e o novo gerasse outra 1000 simultaneamente, a replicação quebraria. A disciplina de geração de IDs foi mais crítica do que a escolha da linguagem de programação.
O legado não é código, é a regra de negócio esquecida
A maior surpresa não veio dos erros de código, mas dos "requisitos ocultos". O monolito carregava consigo regras de negócio que não estavam documentadas em nenhum Jira, mas estavam "hardcoded" em IFs espalhados desde 2015.
Por exemplo, descobrimos tardiamente que contas conjuntas de um determinado estado tinham um tratamento fiscal diferente na hora de calcular o IR sobre rendimento, regra essa que estava enterrada em um método de 300 linhas chamado processarAntiguidade(). Ao migrarmos essa lógica para um serviço de "Taxas" isolado, quebramos a declaração de imposto de cerca de 5.000 clientes. O monitoramento de exceptions não nos avisou, porque o código rodava "certo", só que com o resultado errado.
A solução foi bárbara, mas necessária: mantivemos um engenheiro sênior focado exclusivamente em ler o legado e interrogar os anciões do banco sobre "por que esse código faz isso?". Documentação de API não substitui a memória institucional. Se você vai migrar, preserve o acesso ao código-fonte antigo até o último dia, mesmo que você odeie olhar para ele.
Conclusão
Terminamos os 18 meses com um sistema mais resiliente, sim, mas também com uma arquitetura mais cara e operacionalmente mais complexa. A migração não é um destino, é um começo. O ganho real não está apenas na tecnologia dos microserviços, mas na disciplina de domínio que eles forçam.
A lição que leva para casa, e que muitos diretores de tecnologia ignoram até estourar o orçamento, é que a ferramenta de recuperação de desastres (backup) importa menos do que a sua capacidade de ler o passado. Se você não consegue ler o que foi escrito no legado de ontem, você não consegue garantir o que será escrito no microserviço de amanhã. A consistência de dados em bancos digitais não é um problema de engenharia de software puramente; é um problema de governança e honestidade sobre as janelas de falha que você está disposto a aceitar.