Todo dev já ouviu (ou disse) a frase: "isso aí tem que migrar". Eu também pensei. O cliente operava uma rede de varejo — 40 mil SKUs, lojas físicas e venda online — sobre um ERP legado que escreve arquivos DBF binários e não tem API. Mas a operação inteira confiava naquele sistema: era rápido no balcão, o time sabia usar, e vinte anos de processo estavam moldados nele.
A dor não era o ERP. Era o que os dados presos nele causavam: replicar estoque e histórico de vendas era difícil, relatório complexo era impraticável, e nada chegava em tempo real aos outros sistemas. O sintoma mais caro: venda de balcão demorava a refletir nos canais online, o anúncio vendia produto que já tinha saído da loja, o estoque quebrava e virava pedido cancelado — e cancelamento em marketplace custa reputação.
O desafio que eu me dei: modernizar sem migrar. Dar ao ERP o que ele nunca teve — consultas rápidas, relatórios complexos, integração em tempo real — sem modificá-lo, sem parar a operação e sem perder um movimento de estoque sequer.
O ERP escreve um diário de eventos em arquivos DBF/FPT. Construí um replicador em Python que acompanha esse diário de forma incremental e espelha tudo em PostgreSQL — com watchdog para reiniciar processo travado e fila de retry com dead-letter para nunca engolir erro silenciosamente.
Só essa camada já mudou o jogo: consultas que travavam em DBF ganharam performance de banco moderno. Relatórios que eram impraticáveis passaram a sair em segundos.
Espelho parado é só backup. O valor está em reagir a cada mudança.
Sobre o espelho, o serviço (NestJS) instala os próprios triggers — 17 canais de captura de mudança via LISTEN/NOTIFY do PostgreSQL: produto, preço, estoque, venda, cliente, entrega... Cada movimento de estoque no balcão dispara uma notificação em milissegundos.
Aqui mora a primeira decisão importante: nenhuma notificação é processada inline. O handler só grava o evento numa inbox transacional. Workers fazem o claim com FOR UPDATE SKIP LOCKED — seguro para múltiplas instâncias — e processam em lote, com retry e status por evento.
Na saída, webhooks assinados com HMAC distribuem cada mudança para quem quiser consumir. E foi aí que o projeto virou outra coisa: um sistema fechado tinha se tornado uma plataforma de integração. Hoje, sobre esses webhooks rodam um CRM que leva números em tempo real para financeiro, CEO e vendedores; um dashboard de ruptura de estoque; e a sincronização com Mercado Livre e Shopee. Pedidos fazem o caminho inverso por um outbox normalizado — um formato único que o ERP consome do jeito dele.
A lição mais importante do projeto cabe numa frase: notificação em tempo real perde evento. Sempre.
Processo reinicia, conexão cai, réplica é reconstruída e derruba os triggers junto. Se a sua arquitetura depende de nunca perder um NOTIFY, ela já está quebrada — só ainda não contou para você.
Por isso o sistema tem quatro laços independentes de auto-recuperação:
E uma guarda de ordenação: o saldo só é aplicado se o sequencial do evento for mais novo que o último aplicado — evento atrasado nunca sobrescreve estoque mais recente. Webhook que falha sobe uma escada de retry de 1 minuto a 24 horas antes de cair na dead-letter.
Velocidade vem do evento. Garantia vem da reconciliação. Os dois juntos são o que deixa a operação dormir tranquila.
O cliente manteve o ERP em que confia. A venda de balcão reflete nos canais online quase em tempo real — a quebra de estoque e os cancelamentos deixaram de ser rotina. E um ecossistema inteiro de sistemas modernos roda sobre um software dos anos 90, atravessando falha de réplica, reinício e evento fora de ordem sem perder dados e sem intervenção manual.
Nem toda modernização é uma migração. Às vezes o trabalho de engenharia mais respeitoso — e mais difícil — é construir o novo ao redor do que já funciona.