5 votes

CQRS : projet de notifications hors ordre dans un modèle de lecture ElasticSearch

Nous avons une architecture microservice et appliquons le modèle CQRS. Une commande envoyée à un microservice déclenche un changement d'état de l'application et l'émission de l'événement correspondant sur notre bus Kafka. Nous projetons ces événements dans un modèle de lecture construit avec ElasticSearch.

Jusqu'à présent, tout va bien.

Nos microservices sont finalement cohérents entre eux. Mais à tout moment, ils ne le sont pas (nécessairement). Par conséquent, les événements qu'ils envoient ne sont pas toujours cohérents entre eux non plus.

De plus, pour garantir la cohérence entre un changement d'état de l'application et l'émission de l'événement correspondant, nous persistons dans la DB le nouvel état et l'événement correspondant dans la même transaction (je suis conscient que nous pourrions utiliser la source d'événements et éviter de persister l'état complètement). Un travailleur asynchrone est ensuite chargé d'envoyer ces événements sur le bus Kafka. Ce modèle garantit qu'au moins un événement sera envoyé pour chaque changement d'état (ce qui n'est pas un problème puisque nos événements sont idempotents). Cependant, étant donné que chaque microservice possède sa propre table d'événements et son propre travailleur asynchrone, nous ne pouvons pas garantir que les événements seront envoyés dans l'ordre dans lequel les changements d'état correspondants se sont produits dans leurs microservices respectifs.

EDIT : pour clarifier, chaque microservice a sa propre base de données, sa propre table d'événements et son propre travailleur. Un employé spécifique traite les événements dans l'ordre dans lequel ils ont été enregistrés dans sa table d'événements correspondante, mais des employés différents sur des tables d'événements différentes, c'est-à-dire pour des microservices distincts, n'offrent pas cette garantie.

Le problème se pose lorsqu'il s'agit de projeter ces événements incohérents ou hors séquence provenant de différents microservices dans le même document ElasticSearch.

Un exemple concret : imaginons trois agrégats différents A, B et C (agrégat au sens du Domain Driven Design) gérés par différents microservices :

  • Il existe une relation de plusieurs à plusieurs entre A et B. L'agrégat A fait référence aux racines de l'agrégat B auquel il est lié, mais B n'est pas conscient de ses relations avec A. Lorsque B est supprimé, le microservice gérant A est à l'écoute de l'événement correspondant et annule la liaison de A avec B.
  • De même, il existe une relation "many-to-many" entre B et C. B connaît tous les agrégats de C, mais l'inverse n'est pas vrai. Lorsque C est supprimé, le microservice qui gère B est à l'écoute de l'événement correspondant et annule la liaison entre B et C.
  • C a une propriété "name".

L'un des cas d'utilisation consiste à trouver, via ElasticSearch, tous les agrégats A liés à un agrégat B, lui-même lié à un agrégat C portant un nom spécifique.

Comme expliqué plus haut, les tables d'événements et les travailleurs distincts pourraient introduire des délais variables entre l'émission d'événements provenant de différents microservices. Créer A, B et C et les lier ensemble pourrait par exemple aboutir à la séquence d'événements suivante :

  1. B créé
  2. B lié à C
  3. C créé avec le nom XYZ
  4. Une création
  5. A lié à B

Autre exemple de lot d'événements : supposons que nous ayons initialement les agrégats B et C et que deux commandes soient émises simultanément :

  • supprimer C
  • lier B à C

cela pourrait entraîner les événements suivants :

  1. C supprimé
  2. B lié à C
  3. B délié de C (en réponse à l'événement 1)

Concrètement, nous avons du mal à projeter ces événements dans le(s) document(s) ElasticSearch car les événements font parfois référence à des agrégats qui n'existent plus ou pas encore. Toute aide serait appréciée.

2voto

guillaume31 Points 4409

Je ne pense pas que le problème que vous soulevez soit exclusif à la partie projection de votre système - il peut également se produire entre les microservices A, B et C.

Normalement, le projecteur reçoit C created en même temps que B. Ce n'est qu'à ce moment-là que B peut se lier à C, ce qui rend impossible l'ordre spécifique que vous avez mentionné pour le projecteur.

Cependant, vous avez raison de dire que les messages peuvent arriver dans le mauvais ordre si, par exemple, la communication réseau entre B et C est considérablement plus rapide qu'entre C et le projecteur.

Je n'ai jamais rencontré un tel problème, mais quelques options me viennent à l'esprit :

  • Ne pas imposer les "clés étrangères" au niveau du modèle de lecture. Stockez B avec sa référence C, même si vous ne savez pas grand-chose de C pour l'instant. En d'autres termes, faites en sorte que B bound to C y C created commutatif.

  • Ajouter un causalité ID à vos événements. Cela permet au client de reconnaître et de traiter les messages hors service. Vous pouvez choisir votre propre politique - rejeter, attendre que l'événement de causalité arrive, essayer de traiter quand même, etc. Ce n'est pas trivial à mettre en œuvre, cependant.

  • Les plateformes de messagerie peuvent garantir la commande dans certaines conditions. Vous avez mentionné Kafka, dans le cadre du même sujet et de la même partition. RabbitMQ, je pense, a des prérequis encore plus forts.

    Je ne suis pas un expert en messagerie, mais il semble que les scénarios de communication inter-microservices où cela serait possible soient limités. Cela semble également aller à l'encontre de la tendance actuelle en matière de cohérence éventuelle, où nous avons tendance à favoriser les opérations commutatives (voir CRDT) plutôt que d'assurer un ordre total.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X