Database Engineering

Maîtriser le modélisation des données pour l'Event Sourcing : Projections et évolution du schéma

L'Event Sourcing (ES) est un modèle architectural puissant qui stocke l'état d'une application sous forme de séquence d'événements plutôt que simplement l'état actuel. Bien que l'ES offre des avantages incroyables comme l'auditabilité, les requêtes temporelles et la rejouabilité, il introduit une complexité significative en matière de modélisation des données, notamment concernant la lecture de ces données (projections) et la gestion des changements au fil du temps (évolution du schéma).

Pour les ingénieurs de bases de données et les architectes backend, naviguer à l'intersection des événements immuables et des modèles de lecture modifiables est l'un des aspects les plus difficiles de la construction de systèmes distribués évolutifs. Cet article explore les mécaniques de construction de projections robustes et les stratégies pour faire évoluer les schémas dans un environnement basé sur l'Event Sourcing.

Côté lecture : Concevoir des projections efficaces

Dans une architecture Event Sourcing, le « côté écriture » (Write Side) est composé des racines d'agrégats et des événements. Le « côté lecture » (Read Side) est composé des projections. Une projection est un processus qui consomme des événements et met à jour un magasin optimisé pour la lecture, tel qu'une base de données relationnelle, un magasin de documents (comme MongoDB) ou un moteur de recherche (comme Elasticsearch).

Le défi principal consiste à maintenir ces projections à jour sans dégrader les performances d'écriture. Cela est généralement réalisé grâce à des systèmes asynchrones et à cohérence éventuelle.

Considérons un scénario simple de commande e-commerce. Lorsqu'un utilisateur passe une commande, nous émettons un événement OrderCreated. Un gestionnaire de projection doit écouter cet événement et insérer un enregistrement dans une table orders_view pour une récupération rapide.

class OrderCreatedHandler:
    def handle(self, event: OrderCreated):
        projection_db.insert({
            "order_id": event.order_id,
            "customer_id": event.customer_id,
            "total_amount": event.total,
            "status": "CREATED",
            "created_at": event.timestamp
        })

Cependant, les systèmes réels nécessitent de gérer une logique métier complexe. Un seul événement peut déclencher plusieurs projections. Par exemple, OrderCreated peut mettre à jour le tableau de bord des ventes, le système de réservation des stocks et le système de points de fidélité client. Concevoir ces gestionnaires pour qu'ils soient idempotents est crucial pour éviter les effets secondaires en double lors des tentatives de reconnexion ou des partitions réseau.

Gérer l'évolution du schéma : le piège de l'immuabilité

Le principe fondamental de l'Event Sourcing est qu'une fois qu'un événement est écrit, il ne peut pas être modifié. Cela crée un problème unique : comment faire évoluer notre application lorsque les exigences métier changent ? Si nous refactorisons une classe ou modifions le nom d'un champ, les événements historiques restent dans leur format d'origine.

Nous ne pouvons pas simplement mettre à jour le schéma de la base de données du magasin d'événements car les événements sont immuables. Au lieu de cela, nous devons adopter une stratégie de versionnement et de transformation. Il existe deux approches courantes :

  1. Modifications rétrocompatibles : Ajoutez de nouveaux champs aux événements sans supprimer les anciens. Par exemple, si nous devons suivre un nouveau fournisseur de livraison, nous ajoutons un champ shipping_provider à l'événement OrderShipped. Les événements existants auront ce champ à null, ce que l'application gérera correctement.
  2. Versionnement des événements : Si un changement est cassant (par exemple, changer le code de devise de "USD" à "US"), nous devons créer un nouveau type d'événement, tel que OrderShippedV2. Le système doit alors disposer de la logique pour déterminer quelle version de l'événement traiter.

Voici un exemple de la manière dont un processeur pourrait gérer le versionnement :

def process_event(event):
    if event.type == "OrderShipped" and event.version == 1:
        # Transformer l'événement hérité vers la structure actuelle
        normalized_data = migrate_v1_to_current(event.data)
        return save_event(normalized_data)
    elif event.type == "OrderShippedV2":
        return save_event(event.data)
    else:
        raise UnsupportedEventVersionError()

Rejouer les événements pour la cohérence

L'évolution du schéma ne concerne pas seulement la lecture de nouvelles données ; il s'agit souvent de corriger d'anciennes données. Si vous introduisez un bug dans votre logique de projection ou si vous devez ajouter un nouveau champ à votre modèle de lecture, vous pouvez simplement relancer vos gestionnaires de projection contre le flux d'événements existant.

Cette capacité de « rejouabilité » (replay) est le super-pouvoir de l'Event Sourcing. Elle vous permet de reconstruire votre base de données de lecture à partir de zéro pour vous assurer qu'elle correspond parfaitement au schéma actuel.

def rebuild_projection(event_store, projection_engine):
    # 1. Vider le magasin de projection existant
    projection_engine.clear()
    
    # 2. Diffuser tous les événements historiques
    events = event_store.get_all_events()
    
    # 3. Les traiter à nouveau via la logique de gestionnaire la plus récente
    for event in events:
        projection_engine.process(event)

Conclusion

La modélisation des données dans l'Event Sourcing nécessite un changement d'état d'esprit par rapport aux opérations CRUD traditionnelles. Vous ne concevez plus seulement des tables ; vous concevez une chronologie de faits. En vous concentrant sur des structures d'événements immuables, en mettant en œuvre des stratégies de versionnement robustes et en tirant parti de la puissance de la rejouabilité des événements, vous pouvez construire des systèmes distribués qui sont non seulement résilients, mais aussi adaptables aux besoins métier changeants.

La leçon clé est que l'évolution du schéma est un citoyen de premier ordre dans l'Event Sourcing. Embrassez l'immuabilité des événements, concevez des projections flexibles, et vous débloquerez le plein potentiel de ce puissant modèle architectural.

Share: