Versionnage des événements

Comment faire évoluer les schémas d'événements dans les systèmes event-driven — compatibilité backward/forward, upcasting et schema registry.

Le défi du versionnage des événements

Dans une architecture event-driven, les événements constituent le contrat entre producteurs et consommateurs. Contrairement aux API REST, où le consommateur envoie une requête et reçoit une réponse immédiate, les événements sont publiés et peuvent être consommés par de multiples services — y compris des services qui n’existaient pas encore lorsque l’événement a été défini.

Cela rend le versionnage des événements plus complexe que celui des API : on ne peut pas simplement « déprécier » un événement, car certains consommateurs peuvent retraiter des événements historiques depuis le log.

Évolution des schémas

Changements compatibles (safe changes)

Ces changements ne cassent pas les consommateurs existants :

  • Ajouter des champs optionnels : les consommateurs qui ne connaissent pas le champ l’ignorent
  • Ajouter de nouveaux types d’événements : les consommateurs qui ne les reconnaissent pas les écartent
  • Étendre les plages de valeurs : par exemple, accepter davantage de valeurs dans un enum (si les consommateurs gèrent les valeurs inconnues)

Changements incompatibles (breaking changes)

Ces changements nécessitent une stratégie de migration :

  • Supprimer des champs : les consommateurs qui dépendent du champ échoueront
  • Renommer des champs : équivalent à en supprimer un et en ajouter un autre
  • Changer les types de données : un consommateur qui attend une string et reçoit un number échouera
  • Changer la sémantique : le champ existe toujours mais signifie autre chose

Compatibilité backward et forward

Backward compatibility (nouveaux lecteurs, anciens producteurs)

Un schéma est backward compatible si les consommateurs disposant du nouveau schéma peuvent lire les événements produits avec l’ancien schéma. Cela s’obtient lorsque :

  • Les nouveaux champs ont des valeurs par défaut
  • On ne supprime pas de champs attendus par les nouveaux consommateurs

Forward compatibility (anciens lecteurs, nouveaux producteurs)

Un schéma est forward compatible si les consommateurs disposant de l’ancien schéma peuvent lire les événements produits avec le nouveau schéma. Cela s’obtient lorsque :

  • Les consommateurs ignorent les champs inconnus
  • On ne change pas le type des champs existants

Full compatibility

Un schéma est fully compatible lorsqu’il est à la fois backward et forward compatible. C’est le niveau le plus sûr et celui recommandé dans la plupart des cas.

Event upcasting

L’upcasting est une technique permettant de transformer des événements d’une ancienne version vers une nouvelle version au moment de la lecture. Plutôt que de migrer tous les événements stockés, on applique une transformation lors de leur lecture :

Événement v1 stocké → Upcaster v1→v2 → Le consommateur reçoit le format v2

Fonctionnement

  1. Les événements sont stockés dans leur format d’origine (immuables)
  2. À la lecture, une chaîne d’upcasters est appliquée selon la version de l’événement
  3. Le consommateur reçoit toujours le format le plus récent

Avantages de l’upcasting

  • Ne nécessite pas de migration massive des données
  • Les événements originaux sont préservés (audit trail intact)
  • De nouvelles transformations peuvent être ajoutées de façon incrémentale

Points de vigilance

  • La chaîne d’upcasters peut s’allonger avec le temps (v1→v2→v3→v4)
  • Chaque upcaster doit être déterministe et sans effets de bord
  • Il est conseillé de tester l’ensemble de la chaîne de transformations

Schema Registry

Un Schema Registry est un service centralisé qui stocke et gère les schémas de tous les événements du système. Des outils comme Confluent Schema Registry ou AWS Glue Schema Registry offrent :

Fonctionnalités clés

  • Stockage centralisé : tous les schémas au même endroit
  • Validation de la compatibilité : rejette les nouveaux schémas qui enfreignent la compatibilité configurée
  • Évolution contrôlée : permet de définir des règles de compatibilité par topic / type d’événement
  • Sérialisation/désérialisation : producteurs et consommateurs utilisent le registry pour sérialiser et désérialiser les événements

Niveaux de compatibilité configurables

NiveauDescription
BACKWARDLes nouveaux consommateurs peuvent lire les anciens événements
FORWARDLes anciens consommateurs peuvent lire les nouveaux événements
FULLBackward + Forward
NONEAucune validation de compatibilité

Flux typique

  1. Le producteur enregistre un nouveau schéma dans le registry
  2. Le registry valide la compatibilité avec la version précédente
  3. S’il est compatible, un ID est attribué au schéma
  4. Le producteur sérialise l’événement avec le schema ID
  5. Le consommateur utilise le schema ID pour désérialiser

Bonnes pratiques

  • Incluez toujours un champ de version dans vos événements (schemaVersion, eventVersion)
  • Utilisez des formats qui supportent l’évolution — Avro, Protobuf ou JSON Schema avec des règles claires
  • Configurez la compatibilité FULL comme valeur par défaut dans votre schema registry
  • Concevez des consommateurs tolérants — capables d’ignorer les champs inconnus et de gérer les champs manquants avec des valeurs par défaut
  • Testez la compatibilité avant de déployer des changements de schéma — idéalement dans le pipeline CI/CD
  • Documentez chaque version du schéma avec son changelog et sa date d’introduction