Projetando para a velocidade: em defesa da ingestão de eventos sem schema

SchemaBridge Team · 2025-12-08 · Event Ingestion, JSON, DX

Velocidade vs. tipos estritos em sistemas orientados a eventos. Por que escolhemos JSON bruto em vez de definições de tipos estritas.

O paradoxo do schema: amigo ou inimigo?

No software corporativo tradicional, schemas estritos (SQL, Protobuf, WSDL) são a base da confiabilidade. O contrato é simples: eu defino a forma dos dados, você se adequa a ela, e o compilador garante que nada vai quebrar. Essa abordagem nos serviu bem por décadas em ambientes controlados, onde somos donos das duas pontas do canal. Ela oferece segurança em tempo de compilação, serialização binária eficiente e uma "fonte da verdade" clara para os desenvolvedores.

Mas em um ambiente de integração moderno e de alta velocidade — onde fornecedores SaaS mudam seus payloads toda semana, ERPs legados emitem CSVs "flexíveis" e microsserviços internos nascem e são aposentados todo mês —, schemas rígidos viram uma camisa de força. Eles oferecem segurança ao custo da inércia. Quando o mundo ao seu redor é fluido, um contrato rígido não é um alicerce; é um ponto de falha. O paradoxo do schema é este: quanto mais você tenta proteger seu sistema com tipos estritos, mais frágil o torna quando o mundo externo muda.

A evolução histórica: do COBOL ao JSON

Para entender a demanda por sistemas sem schema, precisamos olhar para a evolução da troca de dados. Nos primórdios da computação em mainframe, os dados eram armazenados em registros de tamanho fixo (os COPYBOOKs do COBOL). Para adicionar um campo, era preciso recompilar todos os programas que liam aquele registro. Era o schema rígido por excelência. Era a era da "mentalidade de máquina única", em que o armazenamento custava tão caro que cada byte precisava ser contabilizado em uma posição fixa. Não havia espaço para hierarquia, nem para opcionalidade, e certamente nenhum para evolução. Cada caractere de um registro era um recurso precioso, e qualquer mudança era um terremoto que exigia semanas de planejamento e testes.

Nos anos 1980 e 90, migramos para os bancos de dados relacionais e o SQL. Foi um enorme avanço, pois introduziu o conceito de relacionamentos estruturados. Mas também introduziu a migração de banco de dados. Adicionar uma coluna significava indisponibilidade, tabelas bloqueadas e uma coordenação cuidadosa entre DBAs e desenvolvedores. O schema continuava sendo um muro que os desenvolvedores precisavam escalar sempre que queriam inovar. Mesmo com a ascensão de ORMs como o Hibernate, a rigidez subjacente da estrutura de tabelas seguiu sendo o árbitro final do que era possível.

Depois vieram o XML e o SOAP, no fim dos anos 90. Eles introduziram o conceito de "tags", permitindo alguma flexibilidade. Era possível adicionar uma tag XML sem necessariamente quebrar o parser. No entanto, a indústria logo acrescentou o XSD (XML Schema Definition) e os WSDLs, que trouxeram a rigidez de volta. Passamos anos brigando com namespaces e complexos barramentos de serviços corporativos que rejeitavam uma mensagem se um único caractere estivesse fora do lugar. Foi a "Idade das Trevas do XML", em que o overhead do schema consumia mais banda do que os próprios dados.

Hoje temos JSON e REST. O JSON é naturalmente flexível. É apenas um mapa de chaves e valores. Ainda assim, nossos instintos de engenharia nos levam a embrulhar essa flexibilidade em tipos estritos (interfaces TypeScript, DTOs Java, schemas Avro). Estamos tentando impor a rigidez dos mainframes dos anos 1970 a eventos de nuvem dos anos 2020. Por quê? Porque temos medo do desconhecido. Temos medo de que um campo ausente derrube nosso serviço. Mas, como veremos, esse medo está sendo tratado com as ferramentas erradas.

A análise financeira: o "imposto oculto" da manutenção de integrações

Vamos quantificar o custo da rigidez de schemas. Em uma organização de engenharia típica, de médio a grande porte, a manutenção de integrações é uma "crise silenciosa". Ela não aparece como item no balanço, mas é um peso enorme sobre a produtividade.

A matemática da manutenção

O padrão é fácil de ver quando você o procura. Uma organização com um grande portfólio de integrações SaaS externas verá fornecedores mudarem seus payloads com regularidade, e cada mudança que quebra um parser estrito dispara o mesmo ciclo: detecção, atualização de DTOs/schemas, testes e um deploy via CI/CD. Algumas horas por quebra, multiplicadas por muitas quebras por ano, somam um dreno substancial e recorrente do tempo de engenheiros seniores.

Mas o custo real é o custo de oportunidade. Enquanto seus engenheiros seniores atualizam DTOs para a última mudança do Stripe, eles não estão construindo a nova funcionalidade de detecção automática de fraude que faz o negócio avançar. Com o tempo, esse imposto se acumula em uma perda acumulada de velocidade que pode deixar uma empresa bem atrás de concorrentes mais ágeis.

A filosofia da fluidez de dados: schema-on-read

Na SchemaBridge, defendemos uma mudança fundamental de mentalidade: schema-on-read.

Em vez de validar os dados no momento da entrada (schema-on-write), primeiro ingerimos a verdade bruta e hierárquica do evento. O payload JSON é persistido com a execução (run) exatamente como chegou, sem um schema declarado previamente. Só aplicamos um schema — ou, mais precisamente, uma transformação — no momento em que um processo de negócio específico precisa dos dados.

Por que a fluidez de dados vence

1. Ingestão sem intervenção: você pode começar a receber eventos de um novo provedor em segundos. Aponte o webhook do provedor para um entry point da SchemaBridge (atendido pela camada de ingestão), e cada evento JSON inicia imediatamente uma execução registrada. O que os dados significam você descobre depois.

2. Seguro contra o desconhecido: se um provedor adiciona hoje um campo de que você não precisa, ele ainda assim fica registrado no JSON bruto. Os payloads brutos são mantidos pela janela de retenção do seu plano; então, se algumas semanas depois você perceber que precisa, sim, daquele campo, a entrada bruta ainda está lá: você pode reexecutar execuções individuais (ou reenviar seus payloads) por um mapeamento atualizado em vez de pedir dados antigos ao provedor.

3. Evolução desacoplada: sua camada de ingestão e sua camada de transformação podem evoluir em ritmos diferentes. Você pode atualizar sua lógica de negócio muitas vezes por dia sem nunca tocar nos seus entry points.

O argumento matemático a favor da vinculação tardia

Em ciência da computação, vinculação tardia (late-binding) é a prática de adiar a resolução de uma identidade ou de um tipo até o momento da execução. É isso que torna linguagens dinâmicas como Ruby ou Python tão poderosas para certas tarefas.

Schema-on-read é vinculação tardia aplicada à sua infraestrutura de dados. Ao adiar o mapeamento, você passa de um grafo rígido (em que cada mudança exige uma reconstrução completa) para um caminho flexível (que se adapta ao terreno à medida que avança).

Matematicamente, o número de mapeamentos possíveis entre $N$ produtores e $M$ consumidores é $N \times M$. Se todos os produtores e consumidores precisam concordar com um schema estrito, você tem um problema de coordenação gigantesco. Se usar uma ponte sem schema com vinculação tardia, reduz o problema a $N + M$ mapeamentos, cada um local e independente. É assim que se alcança uma verdadeira escala horizontal de engenharia.

JSONata: uma aula magna de processamento funcional de eventos

Para tornar o schema-on-read viável, você precisa de uma linguagem projetada para a descoberta. Escolhemos JSONata. JSONata não é apenas uma linguagem de consulta; é um motor de transformação funcional que opera diretamente sobre a hierarquia JSON bruta.

Anatomia de uma expressão JSONata

Considere um payload de um ERP legado que retorna uma lista de pedidos. Cada pedido tem uma estrutura complexa e aninhada. Você quer extrair todos os números de peça dos pedidos acima de US$ 500 que estão atualmente no estado 'SHIPPING'.

O código tradicional (Javascript):

const parts = payload.orders
  .filter(o => o.total > 500 && o.status === 'SHIPPING')
  .flatMap(o => o.items)
  .map(i => i.partNumber);

Esse código é frágil. Se orders for null, ou se items estiver ausente em algum pedido, ele quebra.

A maestria do JSONata:

orders[total > 500][status = 'SHIPPING'].items.partNumber

Essa expressão é tolerante a caminhos. Um pedido sem items simplesmente não contribui com nada, e um payload sem nenhum orders resulta em nada em vez de um crash por ponteiro nulo. Ela "descobre" os dados em vez de "presumi-los".

Duas ressalvas honestas. Primeiro, o JSONata não faz mágica com tipos: se um fornecedor começar a enviar "total": "600" como string, a comparação gera um erro em vez de adivinhar. Segundo, na SchemaBridge, um mapeamento que não produz nenhuma saída faz a etapa falhar em vez de repassar um valor vazio adiante. Isso é proposital: um mapeamento que de repente não casa com nada é quase sempre sinal de que a estrutura upstream mudou, e você quer que isso seja barulhento.

Padrão avançado: seleção profunda de descendentes

Um dos recursos mais poderosos do JSONata é o operador de descendentes **. Ele permite encontrar qualquer chave, não importa onde esteja na hierarquia.

**.tracking_number

Se cada um dos seus provedores downstream aninha os números de rastreamento de um jeito diferente, essa única expressão encontrará todos eles em todas as versões de payload. Essa é a definição de resiliência estrutural. Ela transforma a caça frágil a uma chave específica em uma busca flexível pela verdade.

Padrão avançado: reestruturação de dados em tempo real

O JSONata permite reconstruir o objeto JSON inteiro em uma única passada.

orders.{ "order_id": ID, "summary": $join(items.name, ', ') }

Em código tradicional, isso exige mapeamento de objetos, concatenação de strings e iteração de arrays. Em JSONata, é uma projeção declarativa do estado desejado. Isso é especialmente útil quando você precisa enviar um resumo simplificado de um evento complexo para uma notificação no Slack ou um app móvel.

Operando dados sem tabelas: segurança e validação

Críticos costumam perguntar: "Se não usamos schemas, como impedimos que lixo entre no nosso sistema?"

A resposta é que sem schema != sem validação. Nós apenas movemos a validação para o nível do vertex.

Estudo de caso: a malha de ingestão de dados em 50 regiões

Considere uma frota global de IoT que ingere telemetria de muitas regiões, cada uma rodando uma versão ligeiramente diferente do firmware de seus sensores. Cada região tem seu próprio "dialeto" de JSON.

O desafio

Suponha que a equipe comece com um sistema de ingestão tradicional baseado em SQL e um schema de tabela estrito. Toda vez que uma atualização de firmware é lançada em uma única região, o pipeline de ingestão daquela região quebra, porque o novo firmware adiciona um campo battery_health_v2 para o qual o banco de dados não tem coluna. A equipe de dados entra em modo permanente de "apagar incêndios", executando comandos ALTER TABLE em muitos bancos de produção e perdendo eventos a cada janela de manutenção.

A solução com SchemaBridge

Agora imagine que a mesma equipe adota uma estratégia sem schema.

1. Captura universal: todas as regiões apontam seus dados para o mesmo entry point da SchemaBridge. A camada de ingestão não se importa com o schema; ela persiste cada evento bruto com sua execução.

2. Mapeamento na leitura: a equipe cria uma etapa de normalização por versão de firmware. Arestas condicionais roteiam cada evento bruto para a etapa certa com base no seu campo de versão de firmware.

3. Upgrades sem código: quando uma nova versão de firmware é lançada, não há banco de dados para atualizar. A equipe duplica a etapa existente, atualiza o mapeamento JSONata para incluir os novos campos e faz o deploy.

O resultado provável

Conclusão: velocidade é uma escolha de design

Schemas estritos são uma escolha. Uma escolha de priorizar a segurança estática sobre a velocidade dinâmica. Em um sistema fechado, é uma escolha válida. Em um ecossistema conectado e distribuído, é uma escolha que leva ao fracasso.

Projete seus sistemas para o mundo como ele é — imprevisível, em evolução e hierárquico. Projete para a velocidade. Projete com a SchemaBridge. Ao abraçar a fluidez dos dados, você destrava a capacidade de construir, escalar e inovar em uma velocidade com a qual seus concorrentes — presos em DTOs rígidos e migrações de banco de dados — só podem sonhar.

Veja também: Dominando fan-outs — passando da ingestão à execução com o vertex Spawner, que distribui trabalho de alto volume entre muitos itens com backpressure limitado.

Explorar