DEV Community

Paulo Junior
Paulo Junior

Posted on

D3.js não deveria saber de onde vêm os dados

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
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

Depois o normalizador:

function normalize(data) {
return data.map(d => ({
label: d.name,
value:Number(d.value),
date:newDate(d.date)
  }));
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
);
Enter fullscreen mode Exit fullscreen mode

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
);
Enter fullscreen mode Exit fullscreen mode

Transformação:

INSERT INTO metrics (name,value,date)
SELECT
  name,
value::NUMERIC,
date::DATE
FROM staging_csv;
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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());
Enter fullscreen mode Exit fullscreen mode

O D3 agora trabalha exclusivamente com o modelo final:

d3.select("svg")
  .selectAll("rect")
  .data(data)
  .enter()
  .append("rect");
Enter fullscreen mode Exit fullscreen mode

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)