Table of Contents
Introduction à l'approvisionnement en événements et au CQRS
La séparation des responsabilités en matière d'approvisionnement et de requêtes d'événements (CQRS) est devenue un modèle fondamental pour la construction de systèmes modernes et distribués. Combinés à des architectures sans serveur, ces modèles permettent de dégager une échelle, une résilience et une auditabilité sans précédent.
Sourcing des événements : conserver le changement comme une séquence d'événements
L'approvisionnement en événements est un modèle de persistance des données où chaque changement à l'état de l'application est capturé comme un événement immuable. Au lieu de stocker seulement l'état actuel, le système enregistre un journal chronologique des événements. L'état actuel peut être reconstruit en rejouant ces événements. Cette approche fournit une piste d'audit complète, permet des requêtes temporelles (p. ex., « quel était l'état à une date donnée? ») et simplifie le débogage et la conformité.
Dans un contexte sans serveur, le magasin d'événements doit être très durable, évolutif et peu latence. Les choix courants incluent AWS DynamoDB, Azure Cosmos DB[, ou Google Cloud Firestore[. DynamoDB, avec son mode de capacité à la demande, s'intègre naturellement dans les modèles de facturation sans serveur et peut gérer les flux d'événements de n'importe quel volume.
Martin Fowler , article sur l'Approvisionnement des Événements demeure une référence définitive pour comprendre les nuances du motif.
Structure de l'événement et schéma
Chaque événement doit contenir au minimum : un type d'événement, un horodatage, un identifiant d'agrégat, un numéro de version et une charge utile avec les données qui ont changé. L'utilisation d'un registre de schéma (p. ex. Google Cloud Schema Registry[ ou AWS EventBridge Schema Registry) aide à maintenir la compatibilité en arrière au fur et à mesure que les événements évoluent.
CQRS: Séparer les lectures des écrits
CQRS (Command Query Responsibility Segregation) découple les modèles utilisés pour gérer les commandes (écritures) de celles utilisées pour gérer les requêtes (lectures).Dans une architecture sans serveur, cela signifie déployer des fonctions ou des services distincts : les gestionnaires de commandes écrit, accrochent souvent des événements au magasin d'événements, tandis que les gestionnaires de requêtes lisent à partir de modèles de lecture optimisés – des tables, des vues matérialisées ou des index de recherche généralement dénormalisés.
Cette séparation apporte des avantages importants : les charges de travail d'écriture restent maigres et axées sur la validation et la persistance des événements, tandis que les modèles de lecture peuvent être adaptés pour une récupération rapide, y compris les pré-joins, les regroupements et les capacités de recherche en texte intégral.Les deux côtés communiquent par des mécanismes asynchrones tels que flux d'événements[ ou files d'attente de messages (p. ex., AWS SQS, Azure Queue Storage, Google Cloud Tasks).
Greg Young , original La documentation CQRS fournit un contexte fondamental pour le motif.
Combiner l'approvisionnement en événements et le CQRS dans sans serveur
Lorsqu'elles sont utilisées ensemble, Event Sourcing et CQRS forment un duo puissant : les commandes produisent des événements stockés dans le journal des événements et des projections (ou abonnés) mettent à jour asynchronement les modèles de lecture.
Voici un flux type de système sans serveur, source d'événements :
- L'action de l'utilisateur déclenche une fonction de commande (par exemple, un AWS Lambda derrière API Gateway).
- La fonction de commande valide l'entrée, produit un ou plusieurs événements de domaine, et les ajoute au magasin d'événements (DynamoDB, Cosmos DB, etc.).
- Après avoir ajouté les événements, la fonction publie un message (par exemple, à Amazon EventBridge, Azure Event Grid ou Google Pub/Sub) indiquant que de nouveaux événements sont disponibles.
- Les fonctions de projection[ s'abonnent au flux d'événements et mettent à jour le modèle de lecture (p. ex., une table DynamoDB dénormalisée, un index Elasticsearch ou un cache comme Redis).
- Les fonctions de requête servent les requêtes de lecture directement depuis le modèle de lecture, ne interrogeant jamais le magasin d'événements.
Cette conception assure une cohérence occasionnelle[ entre les côtés écriture et lecture, qui est un compromis de base de CQRS. Dans de nombreux domaines d'activité, la cohérence éventuelle est acceptable et même souhaitable parce qu'elle permet un débit plus élevé et une latence plus faible pour lire.
Exemple : Gestion des commandes de commerce électronique
Considérez un système de commande. Un utilisateur place une commande (commande), qui émet un événement . Une fonction de projection lit cet événement et met à jour un modèle de lecture résumé de commande qui comprend le nom du produit, la quantité et l'état actuel. Une autre projection pourrait mettre à jour un modèle de lecture de l'inventaire. Si l'utilisateur demande plus tard l'historique de commande, la fonction de requête lit à partir du modèle de synthèse pré-construit, évitant les jointures coûteuses ou lit à partir du magasin d'événements bruts.
Mise en œuvre du magasin d'événements dans les bases de données sans serveur
Avec DynamoDB, une approche commune consiste à utiliser une seule table avec une clé primaire composite : (clé de partition) et (clé de tri). Cela permet une récupération rapide de tous les événements pour un agrégat spécifique dans l'ordre.
Pour les charges de travail nécessitant des requêtes croisées, envisager d'utiliser un index secondaire sur le type d'événement ou l'horodatage. Cependant, éviter de scanner l'ensemble du magasin d'événements; ces besoins sont mieux servis par des modèles de lecture dédiés.
Sur Azure, Cosmos DB offre des capacités similaires avec des niveaux de cohérence configurables et une indexation automatique. Le modèle d'approvisionnement Event du du du du du du du du du du ] fournit des conseils spécifiques à cette plateforme.
Concurrence et endettement
L'utilisation de [(p. ex., mise à jour conditionnelle avec vérification de version dans DynamoDB) permet de s'assurer qu'une seule commande réussit par incrément de version. En cas de conflit, la commande peut être réévaluée après avoir relu les derniers événements. Idempotency est assuré en stockant un identifiant unique (par exemple, un ID de corrélation) avec chaque événement, permettant au gestionnaire de commande de détecter les duplicatas et de les rejeter gracieusement.
Construire des modèles de lecture avec des projections
Les projections sont des fonctions qui consomment des événements et mettent à jour un ou plusieurs modèles de lecture. Dans les versions sans serveur, elles sont mieux mises en œuvre comme des fonctions d'événements déclenchées par le bus d'événements. Chaque fonction de projection doit être idémpotente : si un événement est traité plus d'une fois (par exemple, en raison d'une réessayer), la mise à jour du modèle de lecture doit produire le même résultat.
Voici quelques-unes des stratégies communes pour construire des modèles de lecture :
- Tableaux dénormalisés dans DynamoDB ou Cosmos DB qui reflètent les motifs de requête (par exemple, toutes les commandes pour un utilisateur).
- Index de recherche dans Elasticsearch, Amazon OpenSearch ou Azure Rechercher des requêtes en texte intégral et faces.
- matérialisés en utilisant des cadres de streaming comme AWS Kinesis Data Analytics ou Azure Stream Analytics.
- Caches en mémoire (p. ex., ElastiCache, Redis) pour les requêtes ultra-faible latence, avec invalidation basée sur TTL.
Pour éviter un couplage serré, les projections doivent être apatrides et uniquement entraînées par la charge utile de l'événement. Elles peuvent être ajoutées, retirées ou modifiées sans affecter le côté commande.
Manipulation de la cohérence événementielle et des SAGA
Un des plus grands défis d'un système CQRS/ES est de gérer les transactions commerciales en plusieurs étapes et de coordonner les opérations. Un utilisateur peut passer une commande, mais le modèle de lecture ne reflète peut-être pas ce changement pendant quelques centaines de millisecondes. Pour les attentes des utilisateurs synchrones (p. ex., afficher une page de confirmation), le gestionnaire de commande peut retourner immédiatement l'identifiant d'événement pendant que les sondages de frontend pour la mise à jour du modèle de lecture ou s'abonner à un canal WebSocket.
Pour les processus à plusieurs étapes nécessitant des transactions distribuées, le modèle SAGA est la solution préférée. Chaque étape de la saga émet des événements et les événements compensateurs sont stockés dans le magasin d'événements pour annuler des étapes partiellement terminées.
Gestion des erreurs et adéquation à l'échelle
Les environnements sans serveur sont sujets à des défaillances transitoires et à des invocations dupliquées. Les gestionnaires d'événements doivent être conçus pour l'idemppotency. Stockez une fenêtre deduplication[ (p. ex., en utilisant DynamoDB TTL ou un jeu de Redis) qui enregistre les identifiants d'événements traités.
Lorsqu'une commande échoue après l'ajout d'événements au magasin, les événements ont déjà été écrits. Dans de tels cas, vous pouvez avoir besoin d'implémenter un événement compensateur (par exemple, ) pour revenir à l'état. L'événement compensateur est stocké comme un événement normal et déclenche une projection qui annule le travail.
En outre, considérez files d'attentes à lettres mortes (DLQs) pour les événements qui échouent à plusieurs reprises le traitement.
Performance et optimisation des coûts dans les systèmes d'événements sans serveur
Bien que les balances sans serveur automatiquement, l'approvisionnement d'événements conçu avec insouciance peut entraîner des coûts élevés.
- Traitement par lots: Lors de la projection des événements, lire et écrire en lots pour minimiser les requêtes de base de données. DynamoDB=] peut gérer jusqu'à 25 éléments à la fois.
- Snapshots: Entreposez périodiquement des instantanés d'états agrégés pour éviter de rejouer l'intégralité du journal de l'événement à chaque lecture. Les instantanés sont stockés dans la même table de stockage d'événements avec une version spéciale (par exemple, numéro de version précédé de -SNAP). La logique de rejoue commence alors à partir du dernier instantané, réduisant considérablement le temps de lecture.
- Cachage: Cache a fréquemment consulté les données du modèle au niveau de l'application (p. ex., en utilisant ElastiCache ou CloudFront avec du contenu dynamique).
- S partitionnement de l'événement:[ Si vous utilisez un système pub/sous-système comme EventBridge, des événements de partition par type d'agrégat pour contrôler le taux d'invocation des fonctions de projection.
Exemple: Stratégie de snapshot dans DynamoDB
Stockez un instantané avec la clé de partition = agrégatId et triez la clé = ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Test et débogage des systèmes sans serveur d'origine
Les tests d'unité peuvent vérifier que les gestionnaires de commandes produisent les événements appropriés. Les tests d'intégration doivent valider que les projections mettent à jour correctement les modèles de lecture lorsque les événements sont publiés. Puisque les fonctions sans serveur sont apatrides, envisager d'utiliser des émulateurs locaux (p. ex., AWS SAM local, DynamoDB local, EventBridge local teste library) pour exécuter des tests dans des pipelines CI/CD.
Debugging producation issues bénéficies from the event log aself — you can replay events in a development environment to recrée the exact sequence that driving to a bug. Des outils comme AWS X‐Ray ou Azure Monitor[ aident à retracer les invocations de fonctions à travers les services.
Pièges courants et comment les éviter
- Modèle de domaine inappropriée:[ Les activités commerciales ne sont pas toutes des avantages de l'approvisionnement en événements. Si vous avez besoin d'un CRUD simple sans exigences de vérification, les frais généraux peuvent ne pas être justifiés.
- Événements d'une grande taille : Le stockage de charges utiles importantes (p. ex., documents entiers) comme événement unique réduit le rendement.
- Projection drive:[ Lorsque les modèles de lecture deviennent hors de synchronisation en raison d'événements ou de bogues manqués, vous avez besoin d'un mécanisme de replay.
- Ignorer l'évolution du schéma:[ Les événements sont immuables, mais leurs schémas changent. Utilisez un registre et une version de chaque type d'événement. Concevez de nouvelles projections pour gérer plusieurs versions.
- Cold commence à affecter les projections:[ Les fonctions de projection qui sont invoquées peu fréquemment peuvent souffrir de latence de démarrage à froid.
Exemple architectural mondial réel
Une application de négociation financière basée sur AWS Lambda, DynamoDB et EventBridge a mis en place un sourcing événementiel pour enregistrer chaque ordre commercial. Fonctions de commande ont géré les commandes d'achat/vente et émis , et . Projections ont mis à jour une table DynamoDB pour le portefeuille de l'utilisateur et un cluster de recherche élastique pour l'analyse du marché en temps réel. Le système a traité plus de 10 000 événements par seconde pendant les heures de pointe, avec 99,99 % de disponibilité et sous-deuxième latence pour les requêtes de portefeuille, grâce à un snapshoting et une conception de modèle de lecture efficace.
Cette équipe a évité les pièges communs en appliquant la version stricte du schéma d'événement (en utilisant Apache Avro) et en mettant en œuvre un pipeline dédié de replay qui pourrait reconstruire tous les modèles de lecture à partir de zéro en moins de 30 minutes.
Conclusion
En mettant en œuvre Event Sourcing et CQRS dans les architectures sans serveur, vous pouvez développer des systèmes hautement évolutives, auditables et durables. En exploitant des services entièrement gérés pour le stockage d'événements, le routage des messages et le calcul, vous pouvez vous concentrer sur la logique d'affaires tandis que la plateforme s'occupe des problèmes d'infrastructure.