
Integrar sistemas legados sem reescrever tudo
Toda empresa com mais de dez anos de operação tem um sistema legado. Pode ser um ERP dos anos 90 em COBOL, um Oracle E-Business Suite que ninguém quer tocar, um SAP customizado ao ponto de nenhum consultor reconhecer, ou um conjunto de planilhas Excel interconectadas que de alguma forma virou o coração do negócio. Esses sistemas são problemáticos — lentos, caros de manter, impossíveis de integrar com o ecossistema moderno. Mas eles funcionam. E os dados neles são críticos.
A resposta instintiva é "reescrevemos tudo do zero". É também a resposta que mais frequentemente resulta em projetos de anos, orçamentos estourados e sistemas modernos que não funcionam tão bem quanto o legado que substituíram. Há uma razão pela qual o sistema legado ainda existe depois de décadas: ele resolveu problemas reais com muita lógica de negócio embutida que ninguém documentou completamente.
A alternativa é integrar sem substituir — pelo menos não de uma vez.
Strangler Fig Pattern: Modernização Incremental
O padrão Strangler Fig (figo estrangulador) vem da biologia: uma planta que cresce em torno de uma árvore hospedeira, gradualmente substituindo-a enquanto a árvore ainda sustenta o peso. Para sistemas de software, o princípio é o mesmo.
Em vez de substituir o sistema legado de uma vez, você adiciona uma camada moderna ao redor dele. Novas funcionalidades são desenvolvidas no sistema novo. Funcionalidades existentes migram gradualmente, uma por vez, quando há capacidade e necessidade. O legado continua em produção durante toda a transição e é desligado só quando a última funcionalidade migrou.
O processo prático tem três fases:
Fase 1 — Intercepção: adiciona um proxy ou API gateway na frente do sistema legado. Todo tráfego passa por ele. O proxy não faz nada além de repassar as requisições para o legado — por enquanto.
Fase 2 — Migração incremental: funcionalidade por funcionalidade, você implementa a lógica no sistema moderno. O proxy começa a rotear certas requisições para o sistema novo e outras para o legado, dependendo do que já foi migrado.
Fase 3 — Descomissionamento: quando a última funcionalidade migrou, o proxy aponta tudo para o sistema moderno e o legado é desligado.
┌─────────────┐
Usuários ──────────▶│ API Gateway │
└──────┬──────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ Sistema Moderno │ │ Sistema Legado │
│ (funcionalidades│ │ (funcionalidades │
│ já migradas) │ │ ainda não migradas)│
└──────────────────┘ └──────────────────────┘
A vantagem é a reversibilidade: se algo der errado na migração de uma funcionalidade, o proxy volta a rotear para o legado sem impacto para o usuário. Risco controlado, iteração constante.
API Gateway como Camada de Abstração
Para sistemas legados sem API — que é a maioria — o primeiro passo é criar uma camada de API que expõe as funcionalidades do legado em formato moderno (REST ou GraphQL) sem tocar no código legado.
Há três abordagens dependendo de como o legado é acessível:
Wrapping via banco de dados: se o legado usa um banco de dados relacional (Oracle, SQL Server, PostgreSQL), você pode criar uma API que lê e escreve diretamente no banco, ignorando a camada de aplicação. É a abordagem mais rápida, mas perigosa se o banco tem regras de negócio em stored procedures ou triggers.
Screen scraping via automação: se o legado só é acessível via interface gráfica (GUI) e não tem banco acessível, você usa RPA (Playwright, PyAutoGUI) para automatizar a interação com a interface e expõe isso via API. Frágil, mas às vezes é a única opção.
Messaging middleware: para sistemas como SAP e mainframes IBM, existem middlewares específicos (MuleSoft, IBM App Connect, WSO2) que têm conectores prontos para os protocolos nativos desses sistemas (RFC, BAPI, IDoc para SAP; MQ para IBM). Você configura o middleware, e ele expõe o legado via REST.
Um exemplo de wrapper REST simples para um banco Oracle legado usando Python:
from fastapi import FastAPI, HTTPException
from sqlalchemy import create_engine, text
from pydantic import BaseModel
from typing import Optional
import os
app = FastAPI(title="API Legacy Wrapper")
engine = create_engine(os.getenv("ORACLE_DSN"))
class Pedido(BaseModel):
numero: str
cliente_id: str
status: Optional[str] = None
@app.get("/pedidos/{numero}")
async def buscar_pedido(numero: str):
"""Expõe consulta do banco legado via REST moderno"""
with engine.connect() as conn:
result = conn.execute(
text("""
SELECT p.NUM_PED, p.COD_CLI, c.NOM_CLI, p.SIT_PED,
p.VLR_TOT, p.DAT_EMI
FROM TB_PEDIDOS p
JOIN TB_CLIENTES c ON p.COD_CLI = c.COD_CLI
WHERE p.NUM_PED = :numero
"""),
{"numero": numero}
).fetchone()
if not result:
raise HTTPException(status_code=404, detail="Pedido não encontrado")
# Traduz colunas crípticas do legado para nomes modernos
return {
"numero": result.NUM_PED,
"cliente": {
"id": result.COD_CLI,
"nome": result.NOM_CLI
},
"status": traduzir_status(result.SIT_PED),
"valor_total": float(result.VLR_TOT),
"emitido_em": result.DAT_EMI.isoformat()
}
def traduzir_status(codigo: str) -> str:
"""Traduz códigos internos do legado para status legíveis"""
mapa = {
"A": "aberto",
"E": "em_processamento",
"F": "finalizado",
"C": "cancelado"
}
return mapa.get(codigo, "desconhecido")
Esse wrapper não toca no código legado, não muda o banco, não tem risco de regressão. Ele apenas cria uma visão moderna dos dados existentes.
Database Sync: Replicando Dados para Sistemas Modernos
Em muitos casos, o sistema moderno não precisa escrever de volta no legado — ele só precisa ler os dados mais recentes. Nesse cenário, a solução mais simples é sincronizar o banco do legado para um banco moderno (PostgreSQL, por exemplo) e deixar o sistema moderno ler dali.
Duas estratégias principais para sincronização:
Polling com timestamp: a cada N minutos, uma rotina compara o timestamp de atualização dos registros no legado com os que já foram sincronizados. Registros novos ou modificados são copiados para o banco moderno. Simples de implementar, mas com latência de N minutos.
Change Data Capture (CDC) com Debezium: captura as mudanças diretamente do transaction log do banco de dados (binlog do MySQL, WAL do PostgreSQL, redo log do Oracle). Latência de segundos, sem carga adicional nas tabelas de origem. É a abordagem correta para sincronização em tempo real.
Para o polling simples, a estrutura básica:
from datetime import datetime
import sqlalchemy as sa
# Engines para os dois bancos
engine_legado = sa.create_engine("oracle+cx_oracle://...")
engine_moderno = sa.create_engine("postgresql://...")
def sincronizar_pedidos(ultima_sync: datetime) -> int:
"""
Sincroniza pedidos modificados desde a última execução.
Retorna o número de registros sincronizados.
"""
with engine_legado.connect() as conn_orig:
novos = conn_orig.execute(
sa.text("""
SELECT NUM_PED, COD_CLI, SIT_PED, VLR_TOT, DAT_ALT
FROM TB_PEDIDOS
WHERE DAT_ALT > :ultima_sync
ORDER BY DAT_ALT ASC
"""),
{"ultima_sync": ultima_sync}
).fetchall()
if not novos:
return 0
with engine_moderno.connect() as conn_dest:
conn_dest.execute(
sa.text("""
INSERT INTO pedidos (numero, cliente_id, status, valor_total, atualizado_em)
VALUES (:num, :cli, :sit, :vlr, :dat)
ON CONFLICT (numero) DO UPDATE SET
status = EXCLUDED.status,
valor_total = EXCLUDED.valor_total,
atualizado_em = EXCLUDED.atualizado_em
"""),
[{"num": r.NUM_PED, "cli": r.COD_CLI,
"sit": r.SIT_PED, "vlr": r.VLR_TOT,
"dat": r.DAT_ALT} for r in novos]
)
conn_dest.commit()
return len(novos)
O ON CONFLICT DO UPDATE (UPSERT) garante idempotência: se o registro já existe, atualiza. Se não existe, insere. Isso permite reprocessar janelas de tempo sem criar duplicatas.
Event Bridging: CDC com Debezium
Para latência próxima de zero, Debezium é a solução de referência. Ele lê o transaction log do banco de dados e publica cada mudança como um evento em um tópico Kafka. O sistema moderno consome esses eventos em tempo real.
A arquitetura:
Oracle Legado
│
│ redo log (leitura não-invasiva)
▼
┌─────────┐
│ Debezium│ (connector)
└────┬────┘
│ eventos JSON
▼
┌─────────┐
│ Kafka │ (tópico: legado.TB_PEDIDOS)
└────┬────┘
│
├──▶ Sistema Moderno (consumer)
├──▶ Data Warehouse (consumer)
└──▶ Serviço de Notificações (consumer)
O Debezium emite eventos no formato:
{
"op": "u",
"before": { "NUM_PED": "12345", "SIT_PED": "A" },
"after": { "NUM_PED": "12345", "SIT_PED": "F" },
"source": { "table": "TB_PEDIDOS", "ts_ms": 1700000000000 }
}
op pode ser c (create), u (update) ou d (delete). O campo before traz o estado anterior e after o novo estado — você sabe exatamente o que mudou, não apenas que algo mudou.
Conclusão
Sistemas legados não precisam ser um obstáculo eterno. Com as estratégias certas — Strangler Fig para modernização incremental, API Gateway para expor o legado em formato moderno, Database Sync para replicar dados sem impacto, Debezium para CDC em tempo real — você conecta o passado ao presente sem o risco de uma big bang migration.
A chave é começar pela camada de abstração: um API Gateway ou wrapper REST que isola o legado do sistema moderno. A partir daí, a migração acontece de forma controlada, funcionalidade por funcionalidade, com rollback sempre disponível.
Na SystemForge, temos experiência em integrações com sistemas legados de múltiplas gerações — de APIs Oracle a SAP via RFC. Se você precisa conectar um sistema antigo ao seu ecossistema moderno sem parar tudo para uma migração de anos, fale com a gente.
Precisa de Bots e Automações?
Desenvolvemos bots e automações personalizadas para o seu negócio.
Saiba mais →Precisa de ajuda?

