J'ai récemment commencé à plonger dans CQRS / ES parce que je pourrais avoir besoin de l'appliquer au travail. Cela semble très prometteur dans notre cas, car cela résoudrait beaucoup de problèmes.
J'ai esquissé ma compréhension approximative de l'apparence contextuelle d'une application ES / CQRS dans un cas d'utilisation bancaire simplifié (retrait d'argent).
Pour résumer, si la personne A retire de l'argent:
- une commande est émise
- la commande est remise pour validation / vérification
- un événement est poussé vers un magasin d'événements si la validation réussit
- un agrégateur retire l'événement pour appliquer des modifications à l'agrégat
D'après ce que j'ai compris, le journal des événements est la source de la vérité, comme c'est le journal des FAITS, nous pouvons alors en tirer toute projection.
Maintenant, ce que je ne comprends pas, dans ce grand schéma des choses, c'est ce qui se passe dans ce cas:
- règle: un solde ne peut pas être négatif
- la personne A a un solde de 100e
- la personne A émet une commande de retrait de 100e
- passe de validation et l'événement MoneyWithdrewEvent of 100e est émis
- en attendant, la personne A émet une autre commande de retrait de 100e
- le premier MoneyWithdrewEvent n'a pas encore été agrégé, donc la validation passe, car la vérification de validation par rapport à l'agrégat (qui n'a pas encore été mise à jour)
- MoneyWithdrewEvent of 100e est émis une autre fois
==> Nous sommes dans un état incohérent d'un solde étant à -100e et le journal contient 2 MoneyWithdrewEvent
Si je comprends bien, il existe plusieurs stratégies pour faire face à ce problème:
- a) mettre l'id de version agrégée avec l'événement dans le magasin d'événements, donc s'il y a une incompatibilité de version lors de la modification, rien ne se passe
- b) utiliser certaines stratégies de verrouillage, ce qui implique que la couche de vérification doit en créer une
Questions liées aux stratégies:
- a) Dans ce cas, le journal des événements n'est plus la source de la vérité, comment y faire face? De plus, nous sommes revenus au client OK alors que c'était totalement faux de permettre le retrait, est-il préférable dans ce cas d'utiliser des serrures?
- b) Locks == deadlocks, avez-vous une idée des meilleures pratiques?
Dans l'ensemble, ma compréhension est-elle correcte sur la façon de gérer la concurrence?
Remarque: je comprends que la même personne qui retire deux fois de l'argent dans un laps de temps aussi court est impossible, mais j'ai pris un exemple simple, pour ne pas se perdre dans les détails
la source
Réponses:
Ceci est l'exemple parfait d'une application issue d'un événement. Commençons.
Chaque fois qu'une commande est traitée ou réessayée (vous comprendrez, soyez patient), les étapes suivantes sont effectuées:
Application layer
.Aggregate
et le charge à partir du référentiel (dans ce cas, le chargement est effectué en chargeantnew
uneAggregate
instance, en récupérant tous les événements précédemment émis de cet agrégat et en les réappliquant à l'agrégat lui-même; la version de l'agrégat est stockée pour utilisation ultérieure; après l'application des événements, l'agrégat est dans son état final - c'est-à-dire que le solde du compte courant est calculé sous forme de nombre)Aggregate
, likeAccount::withdrawMoney(100)
et collecte les événements produits, c'est-à-direMoneyWithdrewEvent(AccountId, 100)
; s'il n'y a pas assez d'argent dans le compte (solde <100) alors une exception est levée et tout est annulé; sinon, l'étape suivante est effectuée.Aggregate
dans le référentiel (dans ce cas, le référentiel est leEvent Store
); il le fait en ajoutant les nouveaux événements auEvent stream
si et seulement si leversion
duAggregate
est toujours celui qui était lorsque le aAggregate
été chargé. Si la version n'est pas la même, la commande est réessayée - passez à l'étape 1 . Si leversion
est le même, les événements sont ajoutés àEvent stream
et le client reçoit leSuccess
statut.Cette vérification de version est appelée verrouillage optimiste et est un mécanisme de verrouillage général. Un autre mécanisme est le verrouillage pessimiste lorsque d'autres écritures sont bloquées (comme non démarrées) jusqu'à ce que celle en cours soit terminée.
Le terme
Event stream
est une abstraction autour de tous les événements qui ont été émis par le même agrégat.Vous devez comprendre que
Event store
c'est juste un autre type de persistance où sont stockées toutes les modifications apportées à un agrégat, pas seulement l'état final.Le magasin d'événements est toujours la source de la vérité.
En utilisant un verrouillage optimiste, vous n'avez aucun verrou, il suffit de réessayer la commande.
Quoi qu'il en soit, verrous! = Deadlocks
la source
Aggregate
où vous n'appliquez pas tous les événements mais vous gardez un instantané duAggregate
jusqu'à un certain point dans le passé et n'appliquez que les événements qui se sont produits après ce point.Aggregate
, quand l'instantané doit-il être mis à jour? Le magasin d'instantanés est-il le même que le magasin d'événements ou s'agit-il d'une vue matérialisée dérivée du bus d'événements?Fermer. Le problème est que la logique de mise à jour de votre "agrégat" est dans un endroit étrange.
L'implémentation la plus courante est que le modèle de données que votre gestionnaire de commandes conserve en mémoire et le flux d'événements dans le magasin d'événements sont synchronisés.
Un exemple simple à décrire est le cas où le gestionnaire de commandes effectue des écritures synchrones dans le magasin d'événements et met à jour sa copie locale du modèle si la connexion au magasin d'événements indique que l'écriture a réussi.
Si le gestionnaire de commandes doit se resynchroniser avec le magasin d'événements (car son modèle interne ne correspond pas à celui du magasin), il le fait en chargeant l'historique à partir du magasin et en reconstruisant son propre état interne.
En d'autres termes, les flèches 2 et 3 (si présentes) seraient normalement connectées au magasin d'événements, et non à un magasin agrégé.
Les variations de ce sont habituellement le cas - plutôt que annexant au courant dans le flux d'événements, nous avons généralement METTONS à un emplacement spécifique dans le cours d' eau; si cette opération est incompatible avec l'état du magasin, l'écriture échoue et le service peut choisir le mode d'échec approprié (échec au client, réessayer, fusionner ....). L'utilisation d'écritures idempotentes résout un certain nombre de problèmes dans la messagerie distribuée, mais bien sûr, cela nécessite d'avoir un magasin qui prend en charge une écriture idempotente.
la source