Cleverton Bueno
← Voltar para todos os artigos

Construindo um Motor de ETL Flexível: Mapeamento Dinâmico de Planilhas com C++

Este artigo detalha a arquitetura que quebrou a "tirania do layout fixo", criando um sistema de mapeamento dinâmico com um dicionário de sinônimos para ler planilhas de forma inteligente e adaptável.

No nosso artigo anterior, contamos a história de como um processo crítico de carga de dados foi transformado, reduzindo seu tempo de execução de uma semana para apenas 15 minutos (leia também o artigo "e Uma Semana a 15 Minutos: Um Case Prático de Modernização de Processos de Carga de Dados"). A velocidade foi o resultado mais chamativo, mas a verdadeira revolução aconteceu em um nível mais profundo: na arquitetura do software. Hoje, vamos mergulhar no coração dessa mudança e desvendar como quebramos a "tirania do layout fixo".

O Problema: Acoplamento Forte e Código Quebradiço

Todo desenvolvedor que já trabalhou com importação de dados conhece o inimigo: o layout fixo. É o paradigma onde o código é escrito com a premissa de que os dados sempre chegarão em uma ordem e posição exatas. No nosso sistema antigo, o código essencialmente dizia: "Eu espero que o CPF esteja na primeira coluna, o nome na segunda, e o valor na terceira".
Isso cria o que chamamos de acoplamento forte: o código e o formato dos dados estão intrinsecamente ligados. Qualquer mudança no arquivo de entrada – uma coluna movida, um cabeçalho renomeado – quebrava a aplicação. A consequência era um ciclo vicioso de falhas, chamados de suporte e intervenções manuais de desenvolvedores para ajustar um código que nunca foi projetado para se adaptar.

A Nova Arquitetura: Mapeamento por Significado, não por Posição

A virada de chave foi mudar a pergunta fundamental que o software fazia. Em vez de perguntar "O que está na coluna 'A'?", passamos a perguntar "Onde está a informação do 'CPF'?". Essa mudança nos levou a uma arquitetura baseada em metadados, que funciona em duas etapas principais:

  • Definição de "Variáveis de Negócio": Para cada tipo de processo (Renovação, Rendas, Faturamento, etc.), definimos internamente um conjunto fixo de variáveis que representam as informações de que o sistema precisa. Por exemplo, para um script de Renovação Cadastral, as variáveis são [CPF_CNPJ], [MOTIVO], [COOP] e [UA]. Essas variáveis são o "contrato" que o motor de script espera.
  • Mapeamento Dinâmico ("De-Para"): Ao receber uma planilha, o primeiro passo da aplicação é ignorar os dados e ler apenas a linha de cabeçalho. Em seguida, ela tenta associar cada nome de coluna encontrado a uma de suas "variáveis de negócio" internas. A coluna chamada "Documento" na planilha do usuário, por exemplo, seria mapeada para a variável interna [CPF_CNPJ].

Com essa abordagem, a posição física da coluna se torna irrelevante. O que importa é o seu significado, que é estabelecido por essa camada de mapeamento.

Tornando o Mapeamento Inteligente: O Dicionário de Sinônimos

A pergunta seguinte foi: como fazer esse mapeamento de forma automática e eficiente? Sabíamos que os usuários não nomeavam suas colunas de maneira consistente. "CPF", "CPF/CNPJ", "Documento", "Nº do CPF" – todos poderiam se referir à mesma informação. A solução foi criar o que chamo de "dicionário de sinônimos" no coração da aplicação. Em automato.cpp, implementamos a função DevolveIndex(), que é responsável por essa tradução. Ela recebe o nome de uma coluna e, através de uma série de verificações, retorna o código numérico da "variável de negócio" correspondente.
Veja um trecho simplificado da sua lógica:

Snippet de Código 1:

C++: automato.cpp
// Trecho da função DevolveIndex em automato.cpp
int DevolveIndex(AnsiString nome, int pos, int arq)
{
    // Mapeamento para Tipo A (arq == 2)
    if (arq == 2) {
        if (nome == "CPF")
            return 21; // Variável [CPF (Para o TIPO A)]
        if (nome == "MOTIVO")
            return 22; // Variável [MOTIVO (Para o TIPO A)]
        if (nome == "NOME")
            return 24; // Variável [NOME (Para o TIPO A)]
        if (nome == "AGE")
            return 25; // Variável [AGE (Para o TIPO A)]
    }

    // Mapeamento para Tipo B (arq == 3)
    if (arq == 3) {
        if (nome == "CNPJ" || nome == "CGC")
            return 27; // Variável [CNPJ(PARA O TIPO B)]
        if (nome == "MES" || nome == "MÊS")
            return 30; // Variável [MES(PARA O TIPO B)]
        if (nome == "ANO")
            return 31; // Variável [ANO(PARA O TIPO B)]
    }
    // ... dezenas de outras checagens para todos os tipos de arquivo e colunas
    return -1; // Código para "Não reconhecido"
}

Essa função, chamada durante o processo de verificação (verificar()), analisa cada coluna do arquivo Excel. Ao reconhecer "CPF" ou "CPF/CNPJ", ela internamente sabe que aquela coluna corresponde à variável 27. Com essa inteligência, a aplicação se tornou proativa, conseguindo mapear corretamente a grande maioria das planilhas sem qualquer intervenção humana.

Quando a Mágica não é Suficiente: A Intervenção Humana

E se um usuário nomear uma coluna como "Doc." e nosso dicionário não a reconhecer? O sistema antigo simplesmente falharia. O novo sistema, no entanto, entende que isso é uma oportunidade de aprender.
Quando DevolveIndex() retorna -1 (não reconhecido) para uma coluna obrigatória, o fluxo não é interrompido. Em vez disso, a aplicação abre uma tela de associação (TFRela). Nessa tela, o usuário vê a lista de colunas não reconhecidas e, para cada uma, pode selecionar a "variável de negócio" correta em uma lista suspensa.
Ao confirmar, o mapeamento é salvo e o processo continua. Na prática, o usuário "ensina" a aplicação sobre um novo layout de planilha, e essa configuração pode ser reutilizada em futuras importações. É a combinação perfeita entre automação e controle do usuário.

Conclusão: Construindo Sistemas que se Adaptam

A transição de um código rígido para uma arquitetura flexível foi a mudança mais significativa do projeto. Ao combinar um mapeamento automático via "dicionário de sinônimos" com uma interface para intervenção manual, criamos um sistema resiliente, que não quebra, mas se adapta.
O resultado mais profundo dessa abordagem foi a mudança de responsabilidade. A tarefa de lidar com novos layouts de planilha, que antes era um fardo para a equipe de desenvolvimento, tornou-se uma simples configuração realizada pelo próprio usuário. Isso liberou tempo de desenvolvimento, deu autonomia ao usuário e tornou todo o processo de carga de dados infinitamente mais robusto.
Agora que entendemos como o sistema se tornou flexível para entender os dados, a pergunta que fica é: como garantimos que o processamento desses 600.000 registros fosse feito em minutos? No próximo artigo, vamos fundo nas estratégias de I/O e nas otimizações em C++ que nos deram essa performance extrema (leia também o artigo "Otimização Extrema em C++: Acelerando 600.000 Registros").