Partager via


Modèle d’approvisionnement en événements

Azure

Au lieu de stocker uniquement l’état actuel des données dans une base de données relationnelle, stockez la série complète d’actions effectuées sur un objet dans un magasin d’ajout uniquement. Le magasin joue le rôle de système d’enregistrement et peut être utilisé pour matérialiser les objets du domaine. Cette approche peut améliorer les performances, la scalabilité et l’auditabilité dans les systèmes complexes.

Important

L’approvisionnement en événements est un modèle complexe qui implémente l’ensemble de l’architecture et introduit des compromis pour obtenir des performances, une scalabilité et une auditabilité accrues. Une fois que votre système devient un système d’approvisionnement en événements, toutes les futures décisions de conception sont limitées par le fait qu’il s’agit d’un système d’approvisionnement en événements. Il existe un coût élevé pour migrer vers ou depuis un système d’approvisionnement en événements. Ce modèle convient le mieux aux systèmes où les performances et l’extensibilité sont les principales exigences. La complexité que l’approvisionnement en événements ajoute à un système n’est pas justifié pour la plupart des systèmes.

Contexte et problème

La plupart des applications fonctionnent avec des données et l’approche classique consiste à l’application à stocker l’état le plus récent des données dans une base de données relationnelle, en insérant ou en mettant à jour des données selon les besoins. Par exemple, dans le modèle CRUD (Create, Read, Update and Delete) traditionnel, un processus de données classique consiste à lire des données à partir du magasin, à y apporter des modifications et à mettre à jour l’état actuel des données avec les nouvelles valeurs, souvent à l’aide de transactions qui verrouillent les données.

L’approche CRUD est simple et rapide pour la plupart des scénarios. Toutefois, dans les systèmes à charge élevée, cette approche présente quelques défis :

  • Performances : à mesure que le système est mis à l’échelle, les performances se dégradent en raison de conflits pour les ressources et les problèmes de verrouillage.

  • Scalabilité : les systèmes CRUD sont synchrones et les opérations de données bloquent les mises à jour. Cela peut entraîner des goulots d’étranglement et une latence plus élevée lorsque le système est en cours de chargement.

  • Auditabilité : les systèmes CRUD stockent uniquement l’état le plus récent des données. Sauf s’il existe un mécanisme d’audit qui enregistre les détails de chaque opération dans un journal distinct, l’historique est perdu.

Solution

Le modèle d’approvisionnement en événements définit une approche de gestion des opérations sur les données qui est guidée par une séquence d’événements, dont chacun est enregistré dans un magasin d’ajout uniquement. Le code d’application déclenche des événements qui décrivent impérativement l’action effectuée sur l’objet. Les événements sont généralement envoyés à une file d’attente où un processus distinct, un gestionnaire d’événements, écoute la file d’attente et conserve les événements dans un magasin d’événements. Chaque événement représente une modification logique de l’objet, tel que AddedItemToOrder ou OrderCanceled.

Les événements sont conservés dans un magasin d’événements qui joue le rôle de système d’enregistrement (la source de données faisant autorité) sur l’état actuel des données. D’autres gestionnaires d’événements peuvent écouter les événements qui leur intéressent et entreprendre une action appropriée. Les consommateurs pourraient, par exemple, initier des tâches qui appliquent les opérations des événements à d’autres systèmes, ou effectuer toute autre action associée nécessaire pour terminer l’opération. Notez que le code applicatif qui génère les événements est découplé des systèmes qui s’abonnent aux événements.

Les applications peuvent, à tout moment, lire l’historique des événements. Vous pouvez ensuite utiliser les événements pour matérialiser l’état actuel d’une entité en lisant et en consommant tous les événements liés à cette entité. Ce processus peut être effectué à la demande pour matérialiser un objet de domaine lors du traitement d’une requête.

Étant donné qu’il est relativement coûteux de lire et de relire des événements, les applications implémentent généralement des vues matérialisées, des projections en lecture seule du magasin d’événements optimisés pour l’interrogation. Par exemple, un système peut conserver une vue matérialisée de toutes les commandes client utilisées pour remplir l’interface utilisateur. Lorsque l’application ajoute de nouvelles commandes, ajoute ou supprime des éléments sur la commande ou ajoute des informations d’expédition, les événements sont déclenchés et un gestionnaire met à jour la vue matérialisée.

La figure montre une vue d’ensemble du modèle, y compris certaines implémentations classiques avec le modèle, notamment l’utilisation d’une file d’attente, un magasin en lecture seule, l’intégration d’événements à des applications et systèmes externes et la relecture d’événements pour créer des projections de l’état actuel d’entités spécifiques.

Vue d’ensemble et exemple du modèle d'approvisionnement en événements

Flux de travail

Le code suivant décrit un flux de travail classique pour ce modèle :

  1. La couche présentation appelle un objet responsable de la lecture à partir d’un magasin en lecture seule. Les données retournées sont utilisées pour remplir l’interface utilisateur.
  2. La couche présentation appelle des gestionnaires de commandes pour effectuer des actions telles que créer un panier ou ajouter un élément au panier.
  3. Le gestionnaire de commandes appelle le magasin d’événements pour obtenir les événements historiques de l’entité. Par exemple, il peut récupérer tous les événements de panier. Ces événements sont lus dans l’objet pour matérialiser l’état actuel de l’entité, avant toute action effectuée.
  4. La logique métier est exécutée et les événements sont déclenchés. Dans la plupart des implémentations, les événements sont envoyés à une file d’attente ou à une rubrique pour dissocier les producteurs d’événements et les consommateurs d’événements.
  5. Les gestionnaires d’événements écoutent les événements qui les intéressent et effectuent l’action appropriée pour ce gestionnaire. Certaines actions de gestionnaire d’événements classiques sont les suivantes :
    1. Écriture des événements dans le magasin d’événements
    2. Mise à jour d’un magasin en lecture seule optimisé pour les requêtes
    3. Intégration à des systèmes externes

Avantages du modèle

Le modèle d'approvisionnement en événements offre les avantages suivants :

  • Les événements sont immuables et peuvent être conservés au moyen d’une opération d’ajout uniquement. L’interface utilisateur, le flux de travail ou le processus qui a déclenché un événement peut continuer, et les tâches qui gèrent les événements peuvent s’exécuter en arrière-plan. Ce processus, combiné au fait qu’il n’y a pas de contention pendant le traitement des transactions, peut améliorer considérablement les performances et l’évolutivité pour les applications, en particulier pour la couche de présentation.

  • Les événements sont des objets simples qui décrivent certaines actions qui se sont produites, ainsi que toutes les données associées nécessaires pour décrire l’action représentée par l’événement. Les événements ne mettent pas directement à jour un magasin de données. Ils sont simplement enregistrés afin d’être traités au moment opportun. L’utilisation d’événements permet de simplifier la mise en œuvre et la gestion.

  • Les événements ont généralement un sens pour les experts de domaine, tandis que l’anomalie d’impédance objet-relationnel peut rendre difficile à comprendre les tables de base de données complexes. Les tables sont des constructions artificielles qui représentent l’état actuel du système, et non les événements qui se sont produits.

  • L’approvisionnement en événements peut permettre d’éviter que les mises à jour simultanées ne provoquent des conflits car cela évite d’avoir mettre à jour directement des objets dans le magasin de données. Toutefois, le modèle de domaine doit toujours être conçu de manière à se protéger lui-même contre les requêtes pouvant entraîner un état incohérent.

  • Le stockage d’ajout uniquement des événements fournit une piste d’audit pouvant être utilisée pour surveiller les actions effectuées sur un magasin de données. Il peut régénérer l’état actuel sous forme de vues matérialisées ou de projections en relisant les événements à tout moment, et aider à tester et déboguer le système. En outre, la nécessité d’utiliser des événements de compensation pour annuler les modifications peut fournir un historique des modifications qui ont été annulées, ce qui ne serait pas possible si le modèle stockait simplement l’état actuel. La liste des événements peut également être utilisée pour analyser les performances applicatives et détecter les tendances de comportement des utilisateurs, ou pour obtenir d’autres informations utiles.

  • Les gestionnaires de commandes déclenchent des événements et les tâches effectuent des opérations en réponse à ces événements. Ce découplage des tâches à partir des événements favorise la flexibilité et l’extensibilité. Les tâches connaissent le type d’événement et ses données, mais pas l’opération qui a déclenché l’événement. En outre, plusieurs tâches peuvent gérer chaque événement. Cela facilite l’intégration avec d’autres services et systèmes qui écoutent uniquement les nouveaux événements déclenchés par le magasin d’événements. Toutefois, les événements d’approvisionnement en événements ont tendance à être à un très bas niveau et il peut être nécessaire de générer à la place des événements d’intégration spécifiques.

L’approvisionnement en événements est généralement combiné au modèle CQRS en effectuant les tâches de gestion des données en réponse aux événements et en matérialisant les vues à partir des événements stockés.

Problèmes et considérations

Prenez en compte les points suivants lorsque vous choisissez comment implémenter ce modèle :

  • Cohérence éventuelle : le système sera uniquement cohérent lors de la création de vues matérialisées ou de la génération de projections de données en relecture d’événements. Il existe une certaine latence entre le moment où une application ajoute des événements au magasin d’événements suite au traitement d’une requête, la publication des événements et leur traitement par les consommateurs des événements. Pendant ce temps, les nouveaux événements qui décrivent les modifications supplémentaires apportées aux entités peuvent être parvenus au magasin d’événements. Vos clients doivent être d’accord avec le fait que les données sont finalement cohérentes et que le système doit être conçu pour tenir compte de la cohérence éventuelle dans ces scénarios.

    Notes

    Pour plus d’informations sur la cohérence éventuelle, consultez Data Consistency Primer(Manuel d’introduction à la cohérence des données).

  • Événements de contrôle de version : le magasin d’événements est la source permanente d’informations et les données d’événement ne doivent donc jamais être mises à jour. La seule façon de mettre à jour une entité ou d’annuler une modification consiste à ajouter un événement de compensation au magasin d’événements. Si le schéma (plutôt que les données) des événements persistants doit changer, peut-être pendant une migration, il peut être difficile de combiner des événements existants dans le magasin avec la nouvelle version. Votre application doit prendre en charge les modifications apportées aux structures d’événements. Il existe plusieurs manières de procéder.

    • Assurez-vous que vos gestionnaires d’événements prennent en charge toutes les versions d’événements. Il peut s’agir d’un défi pour maintenir et tester. Cela nécessite l’implémentation d’un tampon de version sur chaque version du schéma d’événement pour conserver l’ancien et le nouveau format d’événement.
    • Implémentez un gestionnaire d’événements pour gérer des versions d’événements spécifiques. Il peut s’agir d’un défi de maintenance dans le cas où des modifications de correctif de bogues peuvent être effectuées sur plusieurs gestionnaires. Cela nécessite l’implémentation d’un tampon de version sur chaque version du schéma d’événement pour conserver l’ancien et le nouveau format d’événement.
    • Mettez à jour les événements historiques vers le nouveau schéma lorsqu’un nouveau schéma est implémenté. Cela interrompt l’immuabilité des événements.
  • Ordre des événements : les applications multithreads et plusieurs instances d’applications peuvent stocker des événements dans le magasin d’événements. La cohérence des événements dans le magasin d’événements est indispensable, tout comme l’ordre des événements qui affectent une entité spécifique (l’ordre dans lequel les modifications s’appliquent à une entité affecte son état actuel). L’ajout d’un horodatage à chaque événement peut permettre d’éviter les problèmes. Une autre pratique courante consiste à annoter chaque événement issu d’une requête avec un identificateur incrémentiel. Si deux personnes tentent d’ajouter des événements à la même entité au même moment, le magasin d’événements peut rejeter un événement correspondant à un identificateur d’entité existant et à un identificateur d’événement.

  • Interrogation d’événements : il n’existe aucune approche standard ou des mécanismes existants, tels que des requêtes SQL, pour lire les événements afin d’obtenir des informations. Un flux d’événements constitue les seules données pouvant être extraites en utilisant comme critère un identificateur d’événement. L’ID d’événement correspond généralement à des entités individuelles. L’état actuel d’une entité peut être déterminé uniquement en relisant tous les événements s’y rapportant par rapport à l’état d’origine de cette entité.

  • Coût de la recréation de l’état pour les entités : la longueur de chaque flux d’événements affecte la gestion et la mise à jour du système. Si les flux de données sont volumineux, pensez à créer des instantanés à intervalles réguliers (un nombre spécifié d’événements, par exemple). L’état actuel de l’entité peut être obtenu à partir de l’instantané et en relisant les événements qui se sont produits après ce moment précis. Pour plus d’informations sur la création d’instantanés de données, consultez Réplication de capture instantanée principale-subordonnée.

  • Conflits : même si l’approvisionnement en événements réduit le risque de mises à jour conflictuelles des données, l’application doit toujours être en mesure de traiter les incohérences résultant de la cohérence éventuelle et de l’absence de transactions. Par exemple, un événement indiquant une réduction dans l’inventaire du stock peut arriver dans le magasin de données alors même qu’une commande pour cet élément est en train d’être passée. Il est alors nécessaire de procéder au rapprochement des deux opérations, soit en en informant le client ou bien en créant une commande différée.

  • Besoin d’idempotency : la publication d’événements peut être au moins une fois, et les consommateurs des événements doivent donc être idempotents. Ils ne doivent pas réappliquer la mise à jour décrite dans un événement si celui-ci est traité plusieurs fois. Plusieurs instances d’un consommateur peuvent conserver et agréger la propriété d’une entité, comme le nombre total de commandes passées. Seule une instance doit réussir à incrémenter l’agrégat lorsqu’un événement de passage de commande se produit. Même si ce résultat n’est pas une caractéristique majeure de l’approvisionnement en événements, c’est la décision de mise en œuvre habituelle.

  • Logique circulaire : tenez compte des scénarios dans lesquels le traitement d’un événement implique la création d’un ou plusieurs événements, car cela peut entraîner une boucle infinie.

Quand utiliser ce modèle

Utilisez ce modèle dans les situations suivantes :

  • Lorsque vous souhaitez capturer l’intention, l’objectif ou le motif des données. Par exemple, les modifications apportées à une entité client peuvent être capturées sous la forme d’une série de types d’événements spécifiques comme A déménagé, Compte clôturé ou Décédé.

  • Lorsqu’il est essentiel de limiter ou de prévenir complètement le risque de mises à jour conflictuelles des données.

  • Lorsque vous souhaitez enregistrer les événements qui se produisent pour pouvoir les relire afin de restaurer l’état d’un système, restaurer les modifications ou conserver un historique et un journal d’audit. Par exemple, lorsqu’une tâche implique plusieurs étapes, vous devrez peut-être exécuter certaines actions pour restaurer les mises à jour, puis relire certaines étapes afin de rétablir les données à un état cohérent.

  • Lorsque vous utilisez des événements. Il s’agit d’une caractéristique naturelle du fonctionnement de l’application qui nécessite peu d’effort supplémentaire de développement ou de mise en œuvre.

  • Lorsque vous devez découpler le processus de saisie ou de mise à jour des données des tâches nécessaires à l’application de ces actions. Cette modification peut servir à améliorer les performances de l’interface utilisateur ou distribuer les événements à d’autres écouteurs qui agissent lorsque les événements se produisent. Par exemple, vous pouvez intégrer un système de paie à un site web de soumission des dépenses. Les événements déclenchés par le magasin d’événements en réponse aux mises à jour des données effectuées sur le site web seraient consommés par le site web et le système de paie.

  • Lorsque vous souhaitez disposer de la flexibilité nécessaire pour modifier le format des modèles matérialisés et des données d’entité si les besoins changent, ou en cas d’utilisation avec CQRS, si vous devez adapter un modèle de lecture ou les vues qui exposent les données.

  • Lorsqu’il est utilisé avec CQRS et que la cohérence éventuelle est acceptable lorsqu’un modèle de lecture est mis à jour, ou que l’impact sur les performances de la réalimentation des entités et des données à partir d’un flux d’événements est acceptable.

Ce modèle peut s’avérer inutile dans les situations suivantes :

  • Applications qui ne nécessitent pas de performances ou d’hyper-échelle.

  • Domaines simples ou de petite taille, systèmes ayant peu ou aucune logique d’entreprise, ou systèmes non domaine fonctionnant naturellement bien avec les mécanismes de gestion de données CRUD traditionnels.

  • Systèmes où la cohérence et les mises à jour en temps réel des vues des données sont requises.

  • Systèmes présentant uniquement un faible nombre d’occurrences de mises à jour conflictuelles des données sous-jacentes. Par exemple, les systèmes qui ajoutent surtout des données au lieu de les mettre à jour.

Conception de la charge de travail

Un architecte doit évaluer la façon dont le modèle d’approvisionnement en événements peut être utilisé dans la conception de leurs charges de travail pour se conformer aux objectifs et principes abordés dans les piliers d’Azure Well-Architected Framework. Par exemple :

Pilier Comment ce modèle soutient les objectifs des piliers.
Les décisions relatives à la fiabilité contribuent à rendre votre charge de travail résiliente aux dysfonctionnements et à s’assurer qu’elle retrouve un état de fonctionnement optimal après une défaillance. Grâce à la capture d’un historique des changements dans un processus d’entreprise complexe, il peut faciliter la reconstruction de l’état si vous avez besoin de récupérer des magasins d’états.

- RE :06Partitionnement des données
- RE :09 reprise d’activité après sinistre
L’efficacité des performances permet à votre charge de travail de répondre efficacement aux demandes grâce à des optimisations de la mise à l’échelle, des données, du code. Ce modèle, généralement combiné au CQRS, à une conception appropriée du domaine et à une capture instantanée stratégique, peut améliorer les performances de la charge de travail grâce aux opérations atomiques de type avec ajout seulement et à l’absence de verrouillage de la base de données pour les écritures et les lectures.

- PE :08 Performance des données

Comme pour toute autre décision de conception, il convient de prendre en compte les compromis par rapport aux objectifs des autres piliers qui pourraient être introduits avec ce modèle.

Exemple

Un système de gestion de conférences doit suivre le nombre de réservations effectuées afin de pouvoir vérifier s’il reste des places disponibles lorsqu’un participant potentiel tente d’effectuer une réservation. Le système peut stocker le nombre total de réservations pour une conférence d’au moins deux façons possibles :

  • Il peut stocker les informations sur le nombre total de réservations sous la forme d’une entité distincte dans une base de données contenant des informations de réservation. Lorsque des réservations sont effectuées ou annulées, le système peut incrémenter ou décrémenter ce nombre comme il convient. Cette approche est simple en théorie, mais peut entraîner des problèmes d’extensibilité si un grand nombre de participants tente de réserver des places sur une courte période de temps. Par exemple, dans les derniers jours avant la clôture des réservations.

  • Le système peut stocker des informations sur les réservations et les annulations sous la forme d’événements conservés dans un magasin d’événements. Il peut ensuite calculer le nombre de places disponibles en relisant ces événements. Cette approche peut être plus évolutive en raison de l’immuabilité des événements. Le système doit seulement être en mesure de lire les données du magasin d’événements, ou d’ajouter des données au magasin d’événements. Les informations d’événement sur les réservations et les annulations ne sont jamais modifiées.

Le diagramme suivant illustre la façon dont le sous-système de réservation de places du système de gestion de conférences peut être implémenté grâce à l’approvisionnement en événements.

Utilisation de l’approvisionnement en événements pour capturer des informations sur les réservations de places dans un système de gestion de conférences

Voici la séquence d’actions permettant de réserver deux places :

  1. L’interface utilisateur émet une commande pour réserver des places pour deux participants. La commande est traitée par un gestionnaire de commandes distinct. Logique qui est dissociée de l’interface utilisateur et est responsable du traitement des requêtes publiées sous forme de commandes.

  2. Une entité contenant des informations sur toutes les réservations de la conférence est construite en interrogeant les événements qui décrivent les réservations et les annulations. Cette entité est appelée SeatAvailabilityet est contenue dans un modèle de domaine qui expose des méthodes d’interrogation et de modification des données dans l’entité.

    Certaines optimisations à prendre en compte utilisent des instantanés (afin que vous n’ayez pas besoin d’interroger et de relire la liste complète des événements pour obtenir l’état actuel de l’entité) et de conserver une copie mise en cache de l’entité en mémoire.

  3. Le gestionnaire de commandes appelle une méthode exposée par le modèle de domaine pour effectuer les réservations.

  4. L’entité SeatAvailability déclenche un événement contenant le nombre de sièges réservés. La prochaine fois que l’entité applique des événements, toutes les réservations seront utilisées pour calculer le nombre de sièges restant.

  5. Le système ajoute le nouvel événement à la liste des événements dans le magasin d’événements.

Si un utilisateur annule une place, le système suit un processus similaire, sauf que le gestionnaire de commandes émet une commande qui génère un événement d’annulation de place et l’ajoute dans le magasin d’événements.

En plus d’accroître l’extensibilité, l’utilisation d’un magasin d’événements fournit également un historique complet, ou piste d’audit, des réservations et annulations d’une conférence. Les événements du magasin d’événements constituent l’enregistrement le plus précis. Il n’est pas nécessaire de conserver les agrégats d’une autre manière, car le système peut facilement relire les événements et restaurer l’état à n’importe quel instant donné.

Étapes suivantes

Les modèles et les conseils suivants peuvent aussi présenter un intérêt quand il s’agit d’implémenter ce modèle :

  • Modèle de séparation des responsabilités en matière de commande et de requête (CQRS). Le magasin d’écriture qui fournit la source permanente d’informations relatives à une implémentation CQRS est souvent basé sur une implémentation du modèle d’approvisionnement en événements. Décrit comment séparer les opérations qui lisent les données dans une application des opérations qui mettent à jour les données en utilisant des interfaces distinctes.

  • Modèle de vue matérialisée. Le magasin de données utilisé dans un système basé sur l’approvisionnement en événements n’est généralement pas adaptée pour assurer une interrogation efficace. L’approche courante consiste à générer des vues préremplies des données à intervalles réguliers, ou lorsque les données changent.

  • Modèle de transaction de compensation. Les données existantes dans un magasin d’approvisionnement en événements ne sont pas mises à jour. À la place, de nouvelles entrées sont ajoutées pour assurer la transition de l’état des entités vers les nouvelles valeurs. Pour annuler une modification, on utilise les entrées de compensation car il n’est pas possible d’annuler la modification précédente. Décrit comment annuler le travail qui a été effectué par une opération précédente.