Introdução — o problema real (não é o D3)
Quando começamos a trabalhar com visualização de dados, é comum pensar que o maior desafio está no front-end, especialmente na escolha da biblioteca ou na construção dos gráficos.
Esse problema apareceu numa reunião de trabalho onde a normalização no front-end foi uma escolha inicial, motivada pela simplicidade de implementação. No entanto, ao analisar a escalabilidade e a reutilização da solução, tornou-se evidente que essa abordagem deslocava responsabilidades para o lugar errado do sistema.
Mas, ao longo deste projeto, percebi algo desconfortável:
O problema raramente é o D3.js.
O problema é o formato do dado que chega até ele.
Este artigo mostra como criar um pipeline de dados capaz de consumir CSV, JSON e XML, normalizar tudo em um modelo único e alimentar o mesmo gráfico D3 — sem que ele soubesse (ou precisasse saber) de onde os dados vieram. Para isso, trabalhei com dois cenários diferentes, comparando abordagens e avaliando se essa escolha valia a pena. Vou apresentar ambas as estratégias, incluindo os esquemas e as problemáticas de um cenário onde toda a responsabilidade fica a cargo do front-end.
Abordagem A — A Normalização no Front-end
A primeira abordagem seguiu o caminho mais comum em projetos com D3.js. comumente em projetos pequenos não costumam ter preocupação com escalabilidade — e, por serem muitas vezes acadêmicos, a maioria dos programadores escolhe esse caminho por parecer mais simples. Porém, ao considerar questões de segurança e escalabilidade, essa escolha se mostra ineficaz.
Como funciona
- O front-end identifica o formato dos dados
- Para cada formato, usa um loader diferente (
csv,json,fetch) - Após o carregamento, os dados são normalizados no próprio front-end
- O D3 consome o modelo normalizado
Fluxo:
Fonte de dados
↓
Leitor por formato (CSV /JSON /XML)
↓
Normalizaçãono front-end
↓
Modelo único
↓
D3.js
Essa abordagem funciona bem porque é simples, rápida de implementar e ideal para protótipos e projetos pequenos. Além disso, não exige banco de dados, o que reduz a complexidade inicial e permite validar ideias rapidamente.
Aqui entra código curto, tipo:
// CSV
const csvData = await d3.csv("/data.csv");
// JSON
const jsonData = await d3.json("/data.json");
// XML
const xmlText = awaitfetch("/data.xml").then(res => res.text());
const xml = newDOMParser().parseFromString(xmlText,"text/xml");
Depois o normalizador:
function normalize(data) {
return data.map(d => ({
label: d.name,
value:Number(d.value),
date:newDate(d.date)
}));
}
Mas, com o avanço do projeto, algumas limitações se tornaram evidentes: o front-end passa a conhecer detalhes da origem dos dados, há duplicação de lógica de normalização, o código começa a crescer com condicionais por formato, o gráfico fica acoplado às fontes de dados, e a solução não escala bem para grandes volumes. Ou seja: funciona, mas começa a ficar frágil. Essas limitações motivaram a busca por uma alternativa onde a responsabilidade pela organização dos dados fosse deslocada para uma camada mais adequada do sistema.
Abordagem B — Pipeline de dados no banco
Na segunda abordagem, a responsabilidade pela normalização dos dados foi deslocada para o banco de dados, por meio de uma pipeline de ETL, eliminando do front-end a necessidade de lidar com múltiplos formatos de origem. Com isso, a aplicação cliente passou a consumir apenas um modelo de dados único e consistente, enquanto o D3.js foi utilizado exclusivamente para sua finalidade principal: a visualização. A ideia central dessa abordagem é que o D3.js não deveria saber se os dados vieram de um arquivo CSV, JSON ou XML, mas apenas receber informações já estruturadas no formato correto para renderização.
O que é ETL
O processo de ETL (Extract, Transform, Load) é amplamente definido como uma metodologia para coletar dados de múltiplas fontes, aplicar transformações (limpeza, padronização, enriquecimento) e carregar em um repositório unificado preparado para análise ou consultas eficientes. (AWS, 2026)
Segundo a documentação da Microsoft, ETL consolida dados de diversas fontes em um destino unificado e aplica transformações com base em regras de negócio antes de disponibilizá-los para consumidores posteriores.
A literatura acadêmica moderna também formaliza a necessidade de padrões de integração de dados que garantam consistência, governança, observabilidade e qualidade dos dados ao longo do pipeline.
Como funciona
- O backend identifica a fonte e o formato dos dados
- Cada formato é ingerido por um processo de extração específico (CSV, JSON, XML, API)
- Os dados são armazenados em staging tables sem validação rígida
- A normalização ocorre no banco de dados, por meio de transformações e views
- O front-end consome um modelo único já normalizado
- O D3 utiliza esses dados apenas para visualização
Embora os exemplos apresentados a seguir utilizem banco de dados relacional, a abordagem não é dependente exclusivamente de SQL. O mesmo padrão arquitetural pode ser implementado em bancos NoSQL, utilizando coleções de staging e mecanismos de transformação como aggregation pipelines. O princípio central não está na tecnologia escolhida, mas na separação clara entre dados crus e modelo final consumível pela aplicação.
O pipeline ficou assim:
Fontes de dados (CSV / JSON / XML)
↓
Staging Tables (banco)
↓
Normalização (views)
↓
Modelo único
↓
API simples
↓
D3.js
Nessa abordagem, cada fonte de dados alimenta uma staging table que aceita os dados exatamente como eles são, com tipagem flexível e sem validações rígidas, cujo objetivo não é corrigir ou interpretar os dados, mas apenas capturá-los. Essa etapa cria uma separação clara entre dados crus e dados confiáveis. A partir dessas tabelas intermediárias, o banco de dados assume a responsabilidade pela normalização, utilizando views ou transformações para padronizar campos, aplicar tipagem correta, unificar estruturas e gerar um único modelo de dados consistente.
Exemplo de tabela de staging para CSV:
CREATE TABLE staging_csv (
name TEXT,
value TEXT,
date TEXT
);
Esses dados ainda não estão prontos para análise ou visualização.
Transformação para o modelo final
Após a carga inicial, os dados são transformados e inseridos em uma tabela final, já tipada e otimizada:
CREATE TABLE metrics (
name TEXT,
value NUMERIC,
date DATE
);
Transformação:
INSERT INTO metrics (name,value,date)
SELECT
name,
value::NUMERIC,
date::DATE
FROM staging_csv;
Nesse ponto:
- Datas são datas
- Valores são numéricos
- O modelo é consistente
Essa estrutura é típica de pipelines de ETL que convertem dados brutos de formatos heterogêneos em um modelo relacional consistente preparado para análise.
Indexação e otimização
Como os dados agora estão em um banco relacional, é possível aplicar índices e otimizações:
CREATE INDEX idx_metrics_dateON metrics(date);
CREATE INDEX idx_metrics_nameON metrics(name);
Essas otimizações seriam impossíveis ou custosas no front-end.
Front-end simplificado
Com a pipeline pronta, o front-end passa a consumir apenas um endpoint padronizado:
const data = awaitfetch("/api/metrics").then(res => res.json());
O D3 agora trabalha exclusivamente com o modelo final:
d3.select("svg")
.selectAll("rect")
.data(data)
.enter()
.append("rect");
O D3 não sabe — e não precisa saber — se os dados vieram de CSV, JSON ou XML.
Com esse contrato bem definido, o papel do front-end torna-se simples e bem delimitado: consumir dados já normalizados em JSON, desenhar os gráficos e lidar com a interação do usuário. O front-end deixa de parsear formatos, converter tipos ou decidir a semântica dos dados, e o D3.js passa a atuar apenas como um consumidor de dados prontos para visualização.
Comparação entre as abordagens
| Critério | Normalização no Front | Pipeline de Dados |
|---|---|---|
| Acoplamento | Alto | Baixo |
| Escalabilidade | Limitada | Alta |
| Reuso de dados | Difícil | Fácil |
| Performance | Cliente | Servidor |
| Tipagem forte | Não | Sim |
| D3 focado em visualização | Não | Sim |
Performance no Servidor: Um Custo ou Uma Estratégia?
Como a tabela acima mostra, na abordagem com pipeline de dados, a performance — ou melhor, o custo computacional — fica concentrado no servidor. Pode parecer até contraditório eu defender essa segunda abordagem como a melhor escolha na reunião que tive na empresa. E foi justamente esse ponto que gerou debate. Em conversa com meu professor, surgiu a provocação clássica: “Se eu sou empresário e quero reduzir custo, por que eu colocaria mais carga no servidor?” Faz sentido. Grande parte da evolução do front-end moderno — React, SPAs, renderização no cliente — nasceu da ideia de empurrar processamento para o usuário e aliviar a infraestrutura.
A questão é que nem todo custo é igual. Quando você centraliza a transformação no servidor, ganha controle, segurança dos dados, previsibilidade e capacidade real de otimização (índices, cache, escalabilidade). Quando leva isso para o cliente, você distribui não só processamento, mas também complexidade e inconsistência. Cada usuário vira um “mini processador de ETL” rodando em ambientes que você não controla. Funciona bem no seu notebook. Pode travar no celular do usuário.
E tem mais: no nosso caso, o processamento nem fica 100% concentrado na cloud. A arquitetura prevê um runner rodando na própria infraestrutura do cliente, com mensageria para orquestrar os eventos e cache para acelerar os resultados. Ou seja, não é “jogar CPU na nuvem”, é distribuir processamento de forma consciente e desacoplada. A cloud coordena. O runner executa. O sistema respira.
No fim, o que ficou claro para mim — tanto na reunião quanto na conversa com o professor — é que esse “custo a mais” no servidor pode ser irrisório diante das vantagens arquiteturais. Você troca economia bruta de CPU por estabilidade, reuso de dados, menor acoplamento e melhor experiência do usuário. Em sistemas pequenos talvez não faça tanta diferença. Mas em cenários reais, com volume e crescimento, essa escolha deixa de ser apenas técnica e passa a ser estratégica.
Conclusão
A normalização de dados no front-end não é conceitualmente incorreta e pode ser adequada para protótipos, demonstrações e aplicações de pequena escala, onde simplicidade e rapidez são fatores prioritários. Entretanto, sob a perspectiva da arquitetura de dados, a adoção de uma pipeline estruturada de ETL apresenta vantagens relevantes, como a separação clara de responsabilidades entre as camadas do sistema, maior manutenibilidade, melhor desempenho em cenários de maior volume de dados e o aproveitamento de recursos avançados do banco de dados, incluindo tipagem forte, indexação e agregações otimizadas. Além disso, ao concentrar as transformações no backend, reduzem-se riscos de segurança associados à exposição de regras de negócio, validações sensíveis e dados crus no ambiente do navegador.
Referências Bibliográficas
AWS. O que é ETL? Extração, Transformação e Carga. Amazon Web Services, 2026. Disponível em: https://aws.amazon.com/pt/what-is/etl/. Acesso em: 12 fev. 2026.
MICROSOFT. ETL (extração, transformação e carregamento). Microsoft Learn, 2025. Disponível em: https://learn.microsoft.com/pt-br/azure/architecture/data-guide/relational-data/etl. Acesso em: 12 fev. 2026.
Alura. O que é um pipeline de dados ?. Disponível em: https://www.alura.com.br/artigos/o-que-pipeline-dados. Acesso em: 12 fev. 2026.
RUCCO, C.; SAAD, M.; LONGO, A. Formalizing ETLT and ELTL Design Patterns and Proposing Enhanced Variants: A Systematic Framework for Modern Data Engineering. arXiv, 2025. Disponível em: https://arxiv.org/abs/2511.03393. Acesso em: 12 fev. 2026.
Top comments (0)