Concevoir pour la vélocité : plaidoyer pour l'ingestion d'événements sans schéma

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

Vitesse contre typage strict dans les systèmes événementiels. Pourquoi nous avons choisi le JSON brut plutôt que des définitions de types strictes.

Le paradoxe du schéma : ami ou ennemi ?

Dans le logiciel d'entreprise traditionnel, les schémas stricts (SQL, Protobuf, WSDL) sont le socle de la fiabilité. Le contrat est simple : je définis la forme des données, tu t'y conformes, et le compilateur garantit que rien ne plantera. Cette approche nous a bien servis pendant des décennies dans des environnements contrôlés, où nous maîtrisions les deux extrémités du tuyau. Elle offre une sécurité à la compilation, une sérialisation binaire efficace et une « source de vérité » claire pour les développeurs.

Mais dans un environnement d'intégration moderne et à haute vélocité — où les éditeurs SaaS modifient leurs payloads chaque semaine, où les ERP historiques émettent des CSV « flexibles » et où des microservices internes naissent et disparaissent chaque mois —, les schémas rigides deviennent une camisole de force. Ils apportent la sécurité au prix de l'inertie. Quand le monde autour de vous est fluide, un contrat rigide n'est pas une fondation : c'est un point de défaillance. Le paradoxe du schéma est le suivant : plus vous tentez de protéger votre système par des types stricts, plus vous le rendez fragile lorsque le monde extérieur change.

L'évolution historique : de COBOL à JSON

Pour comprendre la demande de systèmes sans schéma, il faut regarder l'évolution de l'échange de données. Aux débuts de l'informatique mainframe, les données étaient stockées dans des enregistrements de longueur fixe (les COPYBOOK COBOL). Pour ajouter un champ, il fallait recompiler chaque programme qui lisait cet enregistrement. C'était le schéma rigide ultime. C'était l'ère de la « mentalité mono-machine », où le stockage coûtait si cher que chaque octet devait être comptabilisé à une position fixe. Aucune place pour la hiérarchie, aucune pour l'optionnalité, et certainement aucune pour l'évolution. Chaque caractère d'un enregistrement était une ressource précieuse, et tout changement était un séisme qui exigeait des semaines de planification et de tests.

Dans les années 1980 et 1990, nous sommes passés aux bases de données relationnelles et à SQL. Ce fut une avancée considérable, car elle introduisait la notion de relations structurées. Mais elle introduisait aussi la migration de base de données. Ajouter une colonne signifiait une interruption de service, des tables verrouillées et une coordination minutieuse entre DBA et développeurs. Le schéma restait un mur que les développeurs devaient escalader chaque fois qu'ils voulaient innover. Même avec l'essor des ORM comme Hibernate, la rigidité sous-jacente de la structure des tables restait l'arbitre final du possible.

Puis sont arrivés XML et SOAP à la fin des années 90. Ils ont introduit la notion de « balises », qui autorisait une certaine flexibilité. On pouvait ajouter une balise XML sans forcément casser le parser. L'industrie a toutefois vite ajouté XSD (XML Schema Definition) et les WSDL, qui ont ramené la rigidité. Nous avons passé des années à nous battre avec les namespaces et des bus de services d'entreprise complexes qui rejetaient un message au moindre caractère mal placé. C'était « l'âge sombre du XML », où la surcharge du schéma consommait plus de bande passante que les données elles-mêmes.

Aujourd'hui, nous avons JSON et REST. JSON est naturellement flexible. Ce n'est qu'une table de clés et de valeurs. Pourtant, nos réflexes d'ingénieurs nous poussent encore à enfermer cette flexibilité dans des types stricts (interfaces TypeScript, DTO Java, schémas Avro). Nous tentons d'imposer la rigidité des mainframes des années 1970 aux événements cloud des années 2020. Pourquoi ? Parce que nous craignons l'inconnu. Nous craignons qu'un champ manquant fasse planter notre service. Mais, comme nous allons le voir, cette crainte est traitée avec les mauvais outils.

L'analyse financière : la « taxe cachée » de la maintenance des intégrations

Chiffrons le coût de la rigidité des schémas. Dans une organisation d'ingénierie typique, de taille moyenne à grande, la maintenance des intégrations est une « crise silencieuse ». Elle n'apparaît pas comme une ligne du bilan, mais elle pèse lourdement sur la productivité.

Le calcul de la maintenance

Le schéma est facile à repérer dès qu'on le cherche. Une organisation disposant d'un large portefeuille d'intégrations SaaS externes verra les éditeurs modifier régulièrement leurs payloads, et chaque changement qui casse un parser strict déclenche le même cycle : détection, mise à jour des DTO/schémas, tests et un déploiement CI/CD. Quelques heures par rupture, multipliées par de nombreuses ruptures par an, représentent une ponction substantielle et récurrente sur le temps des ingénieurs seniors.

Mais le vrai coût est le coût d'opportunité. Pendant que vos ingénieurs seniors mettent à jour des DTO pour la dernière évolution de Stripe, ils ne construisent pas la nouvelle fonctionnalité de détection automatique de la fraude qui fait avancer l'entreprise. Avec le temps, cette taxe se cumule en une perte de vélocité cumulée qui peut laisser une entreprise loin derrière des concurrents plus agiles.

La philosophie de la fluidité des données : schema-on-read

Chez SchemaBridge, nous prônons un changement d'état d'esprit fondamental : le schema-on-read.

Plutôt que de valider les données au moment de leur entrée (schema-on-write), nous ingérons d'abord la vérité brute et hiérarchique de l'événement. Le payload JSON est persisté avec l'exécution (run) exactement tel qu'il est arrivé, sans schéma déclaré au préalable. Nous n'appliquons un schéma — ou plus exactement une transformation — qu'au moment où un processus métier précis a besoin des données.

Pourquoi la fluidité des données l'emporte

1. Ingestion sans intervention : vous pouvez commencer à recevoir des événements d'un nouveau fournisseur en quelques secondes. Pointez le webhook du fournisseur vers un entry point SchemaBridge (servi par la couche d'ingestion), et chaque événement JSON démarre immédiatement une exécution enregistrée. Ce que signifient les données, vous pourrez le déterminer plus tard.

2. Une assurance contre l'inconnu : si un fournisseur ajoute aujourd'hui un champ dont vous n'avez pas besoin, il est tout de même capturé dans le JSON brut. Les payloads bruts sont conservés pendant la fenêtre de rétention de votre offre ; si vous réalisez quelques semaines plus tard que vous avez finalement besoin de ce champ, l'entrée brute est toujours là : vous pouvez rejouer des exécutions individuelles (ou réinjecter leurs payloads) à travers un mapping mis à jour au lieu de demander d'anciennes données au fournisseur.

3. Évolution découplée : votre couche d'ingestion et votre couche de transformation peuvent évoluer à des rythmes différents. Vous pouvez mettre à jour votre logique métier de nombreuses fois par jour sans jamais toucher à vos entry points.

L'argument mathématique en faveur de la liaison tardive

En informatique, la liaison tardive (late-binding) consiste à retarder la résolution d'une identité ou d'un type jusqu'au moment de l'exécution. C'est ce qui rend des langages dynamiques comme Ruby ou Python si puissants pour certaines tâches.

Le schema-on-read, c'est la liaison tardive appliquée à votre infrastructure de données. En retardant le mapping, vous passez d'un graphe rigide (où chaque changement exige une reconstruction complète) à un chemin flexible (qui peut s'adapter au terrain à mesure qu'il avance).

Mathématiquement, le nombre de mappings possibles entre $N$ producteurs et $M$ consommateurs est $N \times M$. Si chaque producteur et chaque consommateur doivent s'accorder sur un schéma strict, vous avez un problème de coordination massif. Avec un pont sans schéma à liaison tardive, vous ramenez le problème à $N + M$ mappings, chacun local et indépendant. C'est ainsi que l'on atteint une véritable scalabilité horizontale de l'ingénierie.

JSONata : une masterclass du traitement fonctionnel d'événements

Pour rendre le schema-on-read praticable, il faut un langage conçu pour la découverte. Nous avons choisi JSONata. JSONata n'est pas qu'un langage de requête ; c'est un moteur de transformation fonctionnelle qui opère directement sur la hiérarchie JSON brute.

Anatomie d'une expression JSONata

Prenons un payload issu d'un ERP historique qui renvoie une liste de commandes. Chaque commande a une structure complexe et imbriquée. Vous voulez extraire tous les numéros de pièce des commandes dont le montant dépasse 500 $ et qui sont actuellement à l'état 'SHIPPING'.

Le code traditionnel (Javascript) :

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

Ce code est fragile. Si orders est null, ou si items manque pour une commande, il plante.

La maîtrise de JSONata :

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

Cette expression est tolérante aux chemins. Une commande sans items ne contribue simplement à rien, et un payload sans aucun orders se résout en rien plutôt qu'en plantage sur un pointeur nul. Elle « découvre » les données au lieu de les « présupposer ».

Deux réserves honnêtes. D'abord, JSONata ne fait pas de magie avec les types : si un fournisseur se met à envoyer "total": "600" sous forme de chaîne, la comparaison lève une erreur au lieu de deviner. Ensuite, sur SchemaBridge, un mapping qui ne produit aucune sortie fait échouer l'étape au lieu de transmettre une valeur vide en aval. C'est délibéré : un mapping qui ne correspond soudain plus à rien est presque toujours le signe que la forme des données en amont a changé, et vous voulez que cela se voie.

Motif avancé : sélection profonde des descendants

L'une des fonctionnalités les plus puissantes de JSONata est l'opérateur de descendants **. Il permet de trouver n'importe quelle clé, où qu'elle se trouve dans la hiérarchie.

**.tracking_number

Si vos fournisseurs en aval imbriquent tous les numéros de suivi différemment, cette seule expression les retrouvera dans toutes les versions du payload. C'est la définition même de la résilience structurelle. Elle transforme la chasse fragile à une clé précise en une recherche flexible de la vérité.

Motif avancé : restructuration des données à la volée

JSONata permet de reconstruire l'objet JSON entier en une seule passe.

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

En code traditionnel, cela exige du mapping d'objets, de la concaténation de chaînes et de l'itération de tableaux. En JSONata, c'est une projection déclarative de l'état souhaité. C'est particulièrement utile lorsque vous devez envoyer un résumé simplifié d'un événement complexe vers une notification Slack ou une application mobile.

Exploiter des données sans tables : sécurité et validation

Les critiques demandent souvent : « Si nous n'utilisons pas de schémas, comment empêcher les données inutilisables d'entrer dans notre système ? »

La réponse est que sans schéma != sans validation. Nous déplaçons simplement la validation au niveau du vertex.

Étude de cas : le maillage d'ingestion de données sur 50 régions

Imaginez une flotte IoT mondiale qui ingère de la télémétrie provenant de nombreuses régions, chacune exécutant une version légèrement différente du firmware de ses capteurs. Chaque région a son propre « dialecte » JSON.

Le défi

Supposons que l'équipe parte d'un système d'ingestion traditionnel basé sur SQL avec un schéma de table strict. Chaque fois qu'une mise à jour de firmware est déployée dans une seule région, le pipeline d'ingestion de cette région casse, car le nouveau firmware ajoute un champ battery_health_v2 pour lequel la base de données n'a pas de colonne. L'équipe data se retrouve en mode « pompier » permanent, à exécuter des commandes ALTER TABLE sur de nombreuses bases de production et à perdre des événements à chaque fenêtre de maintenance.

La solution SchemaBridge

Imaginez maintenant que la même équipe adopte une stratégie sans schéma.

1. Capture universelle : chaque région envoie ses données vers le même entry point SchemaBridge. La couche d'ingestion ne se soucie pas du schéma ; elle persiste chaque événement brut avec son exécution.

2. Mapping à la lecture : l'équipe crée une étape de normalisation par version de firmware. Des arêtes conditionnelles routent chaque événement brut vers la bonne étape en fonction de son champ de version de firmware.

3. Mises à niveau sans code : lorsqu'une nouvelle version de firmware sort, il n'y a aucune base de données à mettre à jour. L'équipe duplique l'étape existante, met à jour le mapping JSONata pour inclure les nouveaux champs, puis déploie.

Le résultat probable

Conclusion : la vélocité est un choix de conception

Les schémas stricts sont un choix. Le choix de privilégier la sécurité statique sur la vélocité dynamique. Dans un système fermé, c'est un choix valable. Dans un écosystème connecté et distribué, c'est un choix qui mène à l'échec.

Concevez vos systèmes pour le monde tel qu'il est : imprévisible, changeant et hiérarchique. Concevez pour la vélocité. Concevez avec SchemaBridge. En embrassant la fluidité des données, vous gagnez la capacité de construire, de passer à l'échelle et d'innover à une vitesse dont vos concurrents — prisonniers de leurs DTO rigides et de leurs migrations de base de données — ne peuvent que rêver.

Voir aussi : Maîtriser les fan-outs — passer de l'ingestion à l'exécution avec le vertex Spawner, qui répartit un travail à fort volume sur de nombreux éléments avec un backpressure borné.

Explorer