Índice

  1. O problema da integração
  2. Conceitos que você precisa entender
  3. Arquitetura de referência
  4. Passo 1: Mapear dados do ERP
  5. Passo 2: Criar camada de middleware
  6. Passo 3: Implementar sincronização
  7. Passo 4: Webhooks para tempo real
  8. Passo 5: Observabilidade
  9. Erros comuns
  10. Caso real: e-commerce que integrou ERP em 6 semanas

O problema da integração {#problema}

ERP (Enterprise Resource Planning, ou "Sistema de Gestão Empresarial") é o sistema que controla o coração da empresa: pedidos, estoque, financeiro, clientes. Exemplos comuns no Brasil: SAP, TOTVS, RM, e sistemas feitos sob medida.

O problema é que ERPs legados (sistemas antigos, instalados há 10-20 anos) raramente expõem APIs REST modernas. Eles foram desenhados para operar isoladamente, como uma ilha. Suas integrações costumam ser via:

  • Arquivos em disco: o ERP gera um CSV/TXT/XML em uma pasta, outro sistema lê essa pasta periodicamente
  • Tabelas compartilhadas no banco: dois sistemas leem/escrevem na mesma tabela do banco de dados
  • WebServices SOAP: um formato antigo de comunicação baseado em XML, verboso e complexo (SOAP significa Simple Object Access Protocol — irônico, já que não é simples)
  • Conexões diretas ao banco de dados: um sistema acessa o banco do outro diretamente (arriscado e frágil)

Sistemas modernos esperam APIs REST (um padrão de comunicação pela web, usando HTTP — o mesmo protocolo dos sites) com autenticação OAuth (um sistema de "carteirinha" que prova quem você é), respostas em JSON (formato de dados leve e legível) e webhooks (explico adiante).

Analogia: Imagine que o ERP é um funcionário que só fala alemão e se comunica por cartas escritas à mão. O sistema moderno é um funcionário que só fala português e se comunica por WhatsApp. Para eles conversarem, você precisa de um tradutor que entende ambos os idiomas e formatos. Esse tradutor é o que chamamos de middleware.

A lacuna entre os dois mundos é a fonte de 80% dos problemas de integração em empresas. A boa notícia: com a arquitetura certa, essa ponte é construída de forma robusta e manutenível.

Conceitos que você precisa entender {#conceitos}

Antes de mergulhar na implementação, vamos alinhar 6 conceitos-chave:

API REST

Analogia: Uma API é como um garçom de restaurante. Você (o cliente) faz um pedido (request) seguindo o cardápio (os endpoints disponíveis). O garçom leva o pedido à cozinha (o servidor), e traz o prato pronto (a resposta). Você não precisa saber como a cozinha funciona — só precisa saber pedir.

REST (Representational State Transfer) é um conjunto de regras para construir APIs usando HTTP. As operações básicas (CRUD — Create, Read, Update, Delete) mapeiam para verbos HTTP:

OperaçãoVerbo HTTPExemplo
CriarPOSTPOST /pedidos — cria um pedido novo
LerGETGET /pedidos/123 — busca o pedido #123
AtualizarPUT/PATCHPUT /pedidos/123 — atualiza o pedido #123
DeletarDELETEDELETE /pedidos/123 — remove o pedido #123

Middleware

Analogia: Middleware é como um intérprete em uma reunião de negócios internacional. O brasileiro fala português, o alemão fala alemão. O intérprete ouve um, traduz para o outro, e vice-versa. Nenhum dos dois precisa aprender o idioma do outro.

O middleware é a camada que fica entre o ERP e o sistema moderno, traduzindo formatos, protocolos e garantindo que a comunicação flua de forma confiável.

Fila de mensagens (Message Queue)

Analogia: Uma fila de mensagens é como uma caixa de correio comum da empresa. Quando chega uma carta, ela fica na caixa até alguém pegar. Se o carteiro (produtor) entrega uma carta e o destinatário (consumidor) não está, a carta não se perde — fica esperando. O carteiro não precisa esperar o destinatário chegar para ir embora.

Isso é fundamental para integrações: o ERP pode gerar 100 mudanças em 1 minuto. Se tentarmos processar todas síncronas (uma por vez, esperando cada uma terminar), o ERP trava. Com fila, as mudanças entram na fila instantaneamente e são processadas no ritmo do sistema moderno.

Ferramentas comuns: RabbitMQ, Kafka, Amazon SQS.

Webhook

Analogia: Um webhook é como um alarme de incêndio. Em vez de você ficar checando a cada 5 minutos "tem fogo? tem fogo?", o alarme te avisa no momento em que o fogo começa. Você não pergunta — é avisado.

Um webhook é um endpoint (URL) no seu sistema que o ERP chama automaticamente quando algo acontece. Em vez de você perguntar ao ERP a cada minuto "tem pedido novo?" (polling), o ERP te avisa no momento exato em que o pedido é criado.

Idempotência

Analogia: Idempotência é como um carimbo de "PAGO" em uma conta. Se você carimbar uma, dez ou cem vezes, o resultado é o mesmo: a conta está marcada como paga. Não cria múltiplas contas pagas.

Na prática: se a mesma mensagem for processada 2 vezes (por exemplo, devido a um retry), o resultado deve ser o mesmo de processar 1 vez. Não cria duplicatas.

CDC (Change Data Capture)

Analogia: CDC é como uma câmera de segurança que grava tudo que acontece no banco de dados. Em vez de você perguntar "mudou alguma coisa?" a cada minuto, a câmera te entrega um log de tudo que mudou, no momento exato.

Tecnicamente: CDC monitora o log de transações do banco de dados e captura mudanças (insert, update, delete) em tempo real, sem precisar de polling. Ferramenta popular: Debezium.

Arquitetura de referência {#arquitetura}

[ERP Legado]                         [Sistema Moderno]
     │                                      │
     │ (arquivo/SQL/SOAP)                   │ (REST API + JSON)
     ↓                                      ↓
[Adapter ERP] ────→ [Middleware] ────→ [Adapter API]
                          │
                   [Fila de Mensagens (RabbitMQ)]
                          │
                   [Logger + Monitor + Alertas]

Analogia completa: O ERP "fala" gerando arquivos e registros no banco. O Adapter ERP é como um "ouvidor" que captura essas mudanças. O Middleware é o "tradutor" que converte para o formato que o sistema moderno entende. A Fila é a "caixa de correio" que segura as mensagens até serem processadas. O Adapter API é o "garçom" que entrega as mensagens ao sistema moderno.

Princípios da arquitetura:

  1. Adapter Pattern (padrão de adaptador): cada sistema tem um adaptador que traduz seu protocolo. Se o ERP mudar, só o adapter muda — o resto fica intacto.
  2. Assíncrono: comunicação via fila, não síncrona. O ERP não espera o sistema moderno responder.
  3. Idempotente: reprocessar uma mensagem não causa duplicação.
  4. Observável: cada mensagem tem um ID único e é rastreável do início ao fim.

Passo 1: Mapear dados do ERP {#passo-1}

Antes de escrever qualquer código, documente quais dados do ERP serão integrados e como eles estão estruturados.

Analogia: Antes de construir uma ponte entre duas ilhas, você precisa mapear o terreno de cada lado. Quais estradas chegam à margem? Qual o tipo de solo? Sem esse mapeamento, a ponte pode desabar.

# mapeamento-erp.yaml — Documentação dos dados a integrar
entidades:
  pedido:
    origem: tabela TBL_PEDIDO (SQL Server)
    campos:
      - nr_pedido: "ID único do pedido (int, chave primária)"
      - dt_emissao: "Data de emissão (datetime)"
      - cd_cliente: "Código do cliente — FK para TBL_CLIENTE"
      - vl_total: "Valor total (Decimal 12,2 — até 99.999.999,99)"
      - st_pedido: "Status: A=Aberto, F=Faturado, C=Cancelado"
    trigger: "INSERT ou UPDATE em TBL_PEDIDO"
    frequencia: "Aproximadamente 200 pedidos/dia"

  cliente:
    origem: tabela TBL_CLIENTE (SQL Server)
    campos:
      - cd_cliente: "ID único do cliente (int)"
      - nm_cliente: "Nome ou razão social (varchar 100)"
      - nr_cnpj: "CNPJ sem máscara (varchar 14)"
      - ds_email: "E-mail de contato (varchar 200)"
    trigger: "INSERT ou UPDATE em TBL_CLIENTE"
    frequencia: "Aproximadamente 20 clientes novos/dia"

Dica: Use database triggers (gatilhos no banco que disparam quando algo muda) ou CDC (Change Data Capture) para detectar mudanças em vez de polling (ficar perguntando "tem novidade?" a cada minuto). Isso reduz a carga no ERP e diminui o tempo de latência.

Erro comum: Pular o mapeamento e ir direto ao código. Resultado: 3 semanas depois, descobre que o campo st_pedido pode ter 6 valores diferentes, não 3 como você assumiu. O mapeamento é a parte mais importante — invista tempo nele.

Passo 2: Criar camada de middleware {#passo-2}

O middleware é o coração da integração. Ele faz 4 coisas:

  1. mudanças do ERP (via trigger, CDC ou polling)
  2. Transforma para um formato canônico (JSON padronizado que o sistema moderno entende)
  3. Publica em fila (RabbitMQ/Kafka)
  4. Consome da fila e envia para a API moderna

Analogia: O middleware é como uma central de triagem de correios. Recebe cartas de diversos remetentes (ERP), padroniza o formato (envelope padrão), coloca na esteira (fila), e a esteira leva para o destinatário (API moderna) no ritmo certo.

Exemplo em Python (FastAPI + RabbitMQ)

# adapter_erp.py — Adapter que lê mudanças do ERP e publica na fila
import json
from datetime import datetime
from pika import BlockingConnection, ConnectionParameters, BasicProperties

def on_erp_change(entity: str, action: str, data: dict):
    """
    Chamado quando o ERP notifica uma mudança (via trigger ou CDC).

    Args:
        entity: nome da entidade que mudou ('pedido', 'cliente', etc.)
        action: o que aconteceu ('created', 'updated', 'deleted')
        data: os dados do registro que mudou
    """
    # Constrói mensagem no formato canônico (padrão para todos os sistemas)
    message = {
        # ID único da mensagem — usado para idempotência (evitar duplicação)
        "id": f"{entity}-{data['id']}-{action}-{datetime.utcnow().isoformat()}",
        "entity": entity,        # qual entidade mudou
        "action": action,        # o que aconteceu: created | updated | deleted
        "data": data,            # os dados em si
        "timestamp": datetime.utcnow().isoformat(),  # quando ocorreu
    }

    # Conecta ao RabbitMQ e publica a mensagem na fila
    connection = BlockingConnection(ConnectionParameters("rabbitmq"))
    channel = connection.channel()

    # Declara a fila como 'durable' — sobrevive a reinícios do RabbitMQ
    channel.queue_declare(queue="erp_changes", durable=True)

    # Publica a mensagem
    channel.basic_publish(
        exchange="",
        routing_key="erp_changes",
        body=json.dumps(message),
        # delivery_mode=2 = mensagem persistente (não se perde em restart)
        properties=BasicProperties(delivery_mode=2),
    )
    connection.close()
# consumer_api.py — Consumer que lê da fila e envia para a API moderna
import json
from pika import BlockingConnection, ConnectionParameters

def on_message(ch, method, properties, body):
    """
    Chamado quando uma mensagem chega na fila.

    O RabbitMQ chama esta função automaticamente para cada mensagem.
    """
    message = json.loads(body)

    # === IDEMPOTÊNCIA ===
    # Verifica se esta mensagem já foi processada (pode chegar duplicada
    # devido a retries). Usa o ID único da mensagem.
    if already_processed(message["id"]):
        # Já processou? Confirma o recebimento e ignora
        ch.basic_ack(method.delivery_tag)
        return

    # === ENVIO PARA API MODERNA ===
    # Conforme a ação, chama o endpoint correto da API REST
    if message["action"] == "created":
        # POST: cria um novo registro no sistema moderno
        response = api.post(f"/{message['entity']}", json=message["data"])
    elif message["action"] == "updated":
        # PUT: atualiza o registro existente
        response = api.put(
            f"/{message['entity']}/{message['data']['id']}",
            json=message["data"]
        )
    elif message["action"] == "deleted":
        # DELETE: remove o registro
        response = api.delete(f"/{message['entity']}/{message['data']['id']}")

    # === TRATAMENTO DE RESPOSTA ===
    if response.ok:
        # Sucesso: marca como processado (para idempotência) e confirma
        mark_processed(message["id"])
        ch.basic_ack(method.delivery_tag)
    else:
        # Falha: rejeita e coloca de volta na fila (com retry)
        # Em produção: usar dead letter queue após N tentativas
        ch.basic_nack(method.delivery_tag, requeue=True)

Explicação do fluxo: O Adapter ERP detecta uma mudança (ex: novo pedido no ERP) → cria uma mensagem JSON → publica na fila RabbitMQ. O Consumer lê a mensagem da fila → verifica se já processou (idempotência) → envia para a API moderna (POST/PUT/DELETE) → confirma ou rejeita a mensagem.

Passo 3: Implementar sincronização {#passo-3}

Há dois tipos de sincronização: carga completa (one-time, no início) e incremental (contínua, após a carga inicial).

Carga completa (one-time)

A primeira vez que você liga a integração, precisa sincronizar todos os dados históricos. Faça em batches (lotes) para não sobrecarregar o ERP.

-- Carga inicial: busca pedidos em lotes de 10.000
-- OFFSET/FETCH NEXT garante que pegamos os dados em páginas, sem travar o ERP
SELECT * FROM TBL_PEDIDO
WHERE dt_emissao >= '2024-01-01'
ORDER BY nr_pedido
OFFSET 0 ROWS FETCH NEXT 10000 ROWS ONLY;

-- Depois: OFFSET 10000, 20000, 30000... até terminar
# sincronizacao_inicial.py — Carga completa em batches
def sincronizar_pedidos_completos():
    offset = 0
    batch_size = 10000

    while True:
        # Busca um lote de pedidos do ERP
        pedidos = erp_db.execute(
            f"SELECT * FROM TBL_PEDIDO ORDER BY nr_pedido "
            f"OFFSET {offset} ROWS FETCH NEXT {batch_size} ROWS ONLY"
        )

        if not pedidos:
            break  # não há mais dados

        # Envia cada pedido para a fila (mesmo fluxo do adapter)
        for pedido in pedidos:
            on_erp_change("pedido", "created", pedido)

        offset += batch_size
        print(f"Sincronizados {offset} pedidos...")

        # Pausa entre batches para não saturar o ERP
        time.sleep(2)

Incremental (contínuo)

Após a carga inicial, você só precisa sincronizar as mudanças novas:

  • Abordagem 1 — Watermark com timestamp: use o campo updated_at como "marca d'água". A cada 1-5 minutos, busque registros atualizados desde a última marca.
# sincronizacao_incremental.py — Polling com watermark
def sincronizar_incremental():
    # last_watermark = timestamp da última sincronização (salvo em arquivo/banco)
    last_watermark = carregar_ultimo_watermark()

    # Busca apenas registros modificados após o último watermark
    novos_pedidos = erp_db.execute(
        "SELECT * FROM TBL_PEDIDO WHERE updated_at > ? ORDER BY updated_at",
        [last_watermark]
    )

    for pedido in novos_pedidos:
        on_erp_change("pedido", "updated", pedido)

    # Atualiza o watermark para o timestamp do último registro processado
    if novos_pedidos:
        salvar_watermark(novos_pedidos[-1]["updated_at"])
  • Abordagem 2 — CDC (Change Data Capture): usa Debezium + Kafka Connect para capturar mudanças em tempo real, sem polling. Mais eficiente, mas mais complexo de configurar.

Erro comum: Fazer polling a cada 1 segundo. Isso sobrecarrega o ERP com consultas que 99% das vezes não retornam nada. Use intervalos de 1-5 minutos, ou melhor, use CDC/webhooks.

Passo 4: Webhooks para tempo real {#passo-4}

Para notificações em tempo real (sem precisar de polling), use webhooks. O sistema moderno expõe um endpoint que o ERP (ou o middleware) chama quando algo acontece.

// Node.js — endpoint que recebe webhook do sistema moderno
app.post('/webhooks/erp', async (req, res) => {
  const { event, entity, id, data } = req.body;

  // 1. VALIDAR ASSINATURA (segurança obrigatória!)
  // Verifica que o webhook realmente veio de quem diz ter vindo
  // usando HMAC-SHA256 (uma assinatura criptográfica)
  if (!verifySignature(req.headers['x-signature'], req.body)) {
    return res.status(401).send('Invalid signature');
  }

  // 2. COLOCAR NA FILA PARA PROCESSAMENTO ASSÍNCRRONO
  // Não processa aqui — responde 200 rápido e processa depois
  await queue.add('erp_event', { event, entity, id, data });

  // 3. RESPONDER 200 IMEDIATAMENTE
  // Não faz o ERP esperar o processamento terminar
  res.status(200).send('OK');
});

Analogia do webhook: O webhook é como um interfone. Quando alguém toca, você responde "oi, recebido" imediatamente (HTTP 200), e depois vai abrir o portão (processar). Você não faz a pessoa esperar no interfone enquanto você caminha até o portão.

Padrões essenciais de webhook:

  • Sempre responder 200 em menos de 5 segundos: não bloquear quem chama. Se precisar processar algo demorado, coloque em fila e responda 200.
  • Assinar payloads com HMAC-SHA256: para garantir que o webhook veio de quem diz ter vindo (não de um atacante fingindo ser o ERP)
  • Implementar retry exponencial no envio: se o receptor não responder 200, tentar novamente em 1min, 5min, 30min, 2h...
  • Idempotência: mesmo evento enviado 2x não deve criar duplicata

Erro comum: Processar o webhook síncronamente. Se o processamento demora 10 segundos e o ERP tem um timeout de 5 segundos, o ERP acha que falhou e reenvia. Você processa 2 vezes. Solução: responder 200 imediatamente e processar em background.

Passo 5: Observabilidade {#passo-5}

Observabilidade é a capacidade de saber o que está acontecendo com sua integração sem precisar "adivinhar". Integração sem observabilidade é caos em produção — você só descobre que parou quando alguém reclama.

Analogia: Observabilidade é como o painel do carro. Sem ele, você só descobre que acabou a gasolina quando o carro para no meio da estrada. Com o painel, você vê o marcador e abastece antes de ficar sem combustível.

1. Logging estruturado

Cada mensagem deve ser logada com um correlation ID (ID de correlação) que permite rastrear uma mensagem do início ao fim:

{
  "correlation_id": "pedido-12345-created",
  "entity": "pedido",
  "action": "created",
  "status": "success",
  "latency_ms": 142,
  "timestamp": "2026-08-26T10:30:00Z",
  "source": "erp_adapter",
  "target": "api_moderna",
  "message_size_bytes": 1240
}

Por que correlation ID?: Imagine que um pedido falha no sistema moderno. Com o correlation ID, você busca no log e vê exatamente: quando o ERP gerou, quando entrou na fila, quando o consumer processou, onde falhou. Sem isso, você está caçando agulha em palheiro.

2. Métricas (Prometheus + Grafana)

Métricas agregadas que permitem ver tendências e anomalias:

  • integration_messages_total{entity, action, status} — total de mensagens processadas, por entidade/ação/status
  • integration_latency_seconds{entity} — tempo de processamento por entidade
  • integration_queue_depth — quantas mensagens estão na fila esperando
  • integration_error_rate — percentual de mensagens que falharam

3. Alertas

Falhas silenciosas são o pior cenário em integração. Configure alertas automáticos:

CondiçãoAçãoUrgência
Erro rate > 5%Slack/PagerDutyAlta
Queue depth > 1000 mensagensSlackMédia
Latência > 30sInvestigarMédia
Nenhuma mensagem em 1hVerificar ERPAlta

4. Dead Letter Queue (DLQ)

Mensagens que falham N vezes (ex: 5 tentativas) vão para uma fila separada chamada DLQ (Dead Letter Queue, ou "Fila de Cartas Mortas"). Lá, elas não bloqueiam a fila principal e podem ser analisadas manualmente.

Analogia da DLQ: É como a mesa de "correspondência devolvida" nos correios. Cartas que não puderam ser entregues (endereço errado, destinatário ausente) vão para lá, onde um funcionário humano analisa e decide o que fazer. Enquanto isso, o fluxo normal de entregas continua.

Erros comuns {#erros}

Erro 1: Síncrono em vez de assíncrono

O que acontece: No trigger do ERP (que dispara quando um pedido é criado), o código chama a API moderna diretamente e espera a resposta. Se a API moderna cair, ficar lenta ou demorar, o ERP trava — o usuário não consegue salvar o pedido.

O que NÃO fazer:

# ❌ ERRADO: chamada síncrona no trigger do ERP
def on_pedido_created(pedido):
    # Chama a API diretamente e espera a resposta
    response = requests.post("https://api.moderna.com/pedidos", json=pedido)
    response.raise_for_status()  # se falhar, o ERP trava
    # O usuário do ERP fica esperando isso terminar!

O que fazer:

# ✅ CERTO: publica na fila e retorna imediatamente
def on_pedido_created(pedido):
    # Coloca na fila (microsegundos) e retorna
    on_erp_change("pedido", "created", pedido)
    # O ERP continua funcionando normalmente
    # A API moderna será chamada assíncronamente pelo consumer

Erro 2: Sem idempotência

O que acontece: Uma mensagem é processada, mas o basic_ack não chega ao RabbitMQ (problema de rede). O RabbitMQ reenvia a mensagem. O consumer processa de novo → cria um pedido duplicado no sistema moderno.

O que NÃO fazer:

# ❌ ERRADO: sem verificação de idempotência
def on_message(ch, method, properties, body):
    message = json.loads(body)
    # Processa direto, sem verificar se já processou
    api.post(f"/{message['entity']}", json=message["data"])
    ch.basic_ack(method.delivery_tag)
    # Se a mensagem chegar 2x, cria 2 registros!

O que fazer:

# ✅ CERTO: verificar idempotência antes de processar
def on_message(ch, method, properties, body):
    message = json.loads(body)

    # Verifica no banco se este ID já foi processado
    if ja_processado(message["id"]):
        ch.basic_ack(method.delivery_tag)  # confirma e ignora
        return

    api.post(f"/{message['entity']}", json=message["data"])
    marcar_como_processado(message["id"])  # registra no banco
    ch.basic_ack(method.delivery_tag)

Erro 3: Polling agressivo

O que acontece: Para detectar mudanças no ERP, o sistema consulta o banco a cada 1 segundo. Para 99% das consultas, não há nada novo. Isso gera carga desnecessária no banco do ERP, que já tem outros usuários usando.

O que NÃO fazer:

# ❌ ERRADO: polling a cada 1 segundo
while True:
    novos = erp_db.execute("SELECT * FROM TBL_PEDIDO WHERE updated_at > ?", [last_check])
    processar(novos)
    time.sleep(1)  # muito agressivo!

O que fazer:

# ✅ CERTO: intervalo razoável (1-5 min) ou use CDC
# Opção 1: polling a cada 2 minutos (aceitável para a maioria dos casos)
while True:
    novos = erp_db.execute("SELECT * FROM TBL_PEDIDO WHERE updated_at > ?", [last_check])
    processar(novos)
    time.sleep(120)  # 2 minutos

# Opção 2 (melhor): CDC com Debezium — captura mudanças em tempo real
# sem polling. Configura uma vez e esquece.

Erro 4: Sem tratamento de erro — mensagens se perdem

O que acontece: A API moderna retorna erro 500 (servidor indisponível). O código não trata o erro, a mensagem é confirmada (basic_ack) e perdida. O pedido existe no ERP mas não no sistema moderno — inconsistência silenciosa.

O que NÃO fazer:

# ❌ ERRADO: confirma a mensagem mesmo se a API falhar
def on_message(ch, method, properties, body):
    message = json.loads(body)
    try:
        api.post(f"/{message['entity']}", json=message["data"])
    except Exception:
        pass  # ignora o erro!
    ch.basic_ack(method.delivery_tag)  # confirma mesmo assim — mensagem perdida!

O que fazer:

# ✅ CERTO: rejeita a mensagem em caso de erro (volta para a fila)
def on_message(ch, method, properties, body):
    message = json.loads(body)
    try:
        response = api.post(f"/{message['entity']}", json=message["data"])
        response.raise_for_status()  # lança exceção se status >= 400
        ch.basic_ack(method.delivery_tag)
    except Exception as e:
        logger.error(f"Falha ao processar {message['id']}: {e}")
        # Rejeita e coloca de volta na fila para retry
        # Após N falhas, vai automaticamente para DLQ
        ch.basic_nack(method.delivery_tag, requeue=True)

Erro 5: Mapeamento rígido de campos

O que acontece: O ERP adiciona um campo novo na tabela de pedidos (ex: vl_desconto). A integração espera um schema fixo e quebra ao encontrar o campo novo.

O que NÃO fazer:

# ❌ ERRADO: schema rígido que quebra com qualquer mudança
def transformar_pedido(erp_row):
    return {
        "id": erp_row["nr_pedido"],          # se esse campo sumir, quebra
        "data": erp_row["dt_emissao"],        # se renomear, quebra
        "cliente": erp_row["cd_cliente"],     # se mudar tipo, quebra
        "total": erp_row["vl_total"],
        # não há como receber campos novos sem mudar o código
    }

O que fazer:

# ✅ CERTO: schema flexível com campos opcionais
def transformar_pedido(erp_row):
    result = {
        "id": erp_row.get("nr_pedido"),
        "data": erp_row.get("dt_emissao"),
        "cliente": erp_row.get("cd_cliente"),
        "total": erp_row.get("vl_total"),
    }
    # Campos extras do ERP são preservados (não quebra se ERP adicionar campos)
    extras = {k: v for k, v in erp_row.items() if k not in result}
    result["_extras"] = extras  # campos não mapeados ficam aqui
    return result

Erro 6: Sem monitoramento

O que acontece: A integração para de funcionar (RabbitMQ reiniciou, API mudou versão, senha expirou). Ninguém percebe. O cliente liga reclamando que "o sistema não está atualizando". Você descobre o problema 3 dias depois, com dados desatualizados.

O que fazer: Monitoramento proativo é obrigatório. Configure alertas para:

  • Erro rate > 5% → alerta imediato
  • Nenhuma mensagem processada em 1h → verificar se ERP/middleware está vivo
  • Queue depth crescendo → consumer pode estar travado
  • Latência aumentando → possível degradação

Caso real: e-commerce que integrou ERP em 6 semanas {#caso-real}

Um e-commerce de moda (faturamento R$ 8M/ano) usava um ERP TOTVS e uma plataforma de e-commerce (VTEX). Os dados eram sincronizados manualmente: um funcionário exportava CSVs do ERP e importava na VTEX, 3x ao dia.

Problemas:

  • Estoque desatualizado: clientes compravam produtos sem estoque
  • Preços desincronizados: preço promocional na VTEX não refletia no ERP
  • 1 funcionário full-time só fazendo export/import manual
  • Erros frequentes: planilha errada, importação duplicada

Solução implementada (6 semanas):

  1. Semana 1-2: Mapeamento de entidades (produto, estoque, preço, pedido)
  2. Semana 2-3: Adapter ERP lendo mudanças via trigger no SQL Server
  3. Semana 3-4: Middleware + RabbitMQ + consumer enviando para VTEX API
  4. Semana 4-5: Webhook da VTEX para enviar pedidos de volta ao ERP
  5. Semana 5-6: Observabilidade (logs, métricas, alertas) + testes

Resultados:

  • Sincronização em tempo real (< 30 segundos do ERP → e-commerce)
  • Estoque sempre atualizado: vendas de produtos sem estoque: 0
  • 1 funcionário realocado para tarefas de maior valor
  • Erros de sincronização: reduzidos em 98%
  • ROI em 2 meses (economia de salário + redução de chargebacks)

A lição: A integração bem feita não é apenas técnica — ela resolve problemas de negócio reais (estoque desatualizado = cliente frustrado = chargeback = prejuízo).


Precisa integrar seu ERP com sistemas modernos? A Inicialize Tec implementa integrações robustas com middleware, filas e observabilidade. Conversamos sobre seu caso.