12) "Event Sourcing"

Nous avons des ersatzs d'événements au sein de notre PartieDeChasse.
Ceux-ci sont extrêmement limités :
ils ne portent aucune sémantique métier
pas structurés : ce sont de simples
string
On va revoir cette gestion des événements et allons en profiter pour Event-sourcer notre Aggregate. Celà signifie que nous n'allons plus stocker l'état de notre Aggregate mais tous ses événements.
Pour celà, on va :
Prendre du temps pour découvrir ce qu'est l'
Event SourcingQuelques classes ont déjà été implémenté afin de faciliter l'utilisation d'1
Event Storein memoryFaire 1
checkoutdu commit6efde7c3e470e7c84c50da2715c255bd9acd3d6c
Cette version est très minimaliste et ne résolve pas des problématiques telles que la concurrence
Prendre du temps pour comprendre le code du
Domain.CoreCe code est fortement inspiré du travail fait sur
NEventStorePour comprendre comment utiliser ce code, on peut se focaliser sur les tests qui nous en donnent une bonne idée
Identifier quels sont les éléments fondamentaux à mettre en place pour avoir 1
Aggregate"Event-Sourcé"
Changer l'implémentation de Prendre LApéro
Prendre LApéroFaire en sorte que le flux ressemble à cela :

Pour le moment au sein de notre
Domainson implémentation ressemble à ça :
Soyons plus explicite en retournant
Either<Error, Unit>On ne stockera plus l'état mais que les
Eventsdonc plus besoin de retourner le nouvel état de l'objet
🔴 On commence par adapter 1 test existant afin de spécifier nos attentes vis-à-vis du système
On change le test
On utilise 1 verbe au passé pour décrire notre événement -> quelque chose d'immuable

On génère l'Event "structuré" depuis le test
On choisi d'utiliser 1
recordparce qu'ils sont immuables par designOn ajoute la référence sur le projet
Domain.Core
On doit maintenant ajouter 1
overloadsur notre classPartieDeChasseAssertionsafin de pouvoir faire des assertions sur des Domain Events :
On doit vérifier que l'événemt a bien été émis par notre aggrégat et commité dans notre
EventStoreOn va donc vérifier que dans le
streamd'events associé à l'instance de notre aggrégat notreeventest bien présentOn ajoute alors dans l'assertion 1 dépendance sur le repository
Pour continuer on doit itérer sur notre
interfacede repositoryOn commence à inclure de l'asynchronisme en utilisant 1
OptionAsync(toujours deLanguageExt)On adapte aussi l'assertion (on va utiliser l'
AsyncHelperpour se faciliter la tâche)
On ajoute 1 instance d'
EventStoreau sein duPartieDeChasseRepositoryForTestsSachant que pour les besoins de testing nous utiliserons l'instance
InMemoryCe repository permettra de manière transiente de pouvoir faire du state-based et de l'event sourcing
On adapte ses instantiations
On doit maintenant travailler sur l'Aggregate

On lui fait hériter de
Aggregateet on fixe les warnings
🔴 On fail maintenant plus pour des erreurs de compilation mais bien parce qu'aucun événement n'est présent dans l'
Event Store

🟢 On
raisel'event
🔵 On va désormais adapté notre code pour faire en sorte que cet événement puisse être rejoué sur l'aggrégat
La transition (mutation / changement d'état doit se faire au chargement de l'event) ici
Status = Apéro
🔵 On va changer le retour de la méthode à partir de l'appelant
🔵 Quoi d'autre ?
On peut changer l'organisation du
Domainafin de grouper ensemble les couplesCommand | Event

En faisant cela, on brise une règle d'architecture définie précédemment :

On va alors créer 1
ADR(Architecture Decision Record) pour expliquer pourquoi on a voulu dévier de cette règlePlus d'informations sur les ADR ici
Bien sûr, ce genre de décisions doivent être discutées et prises en équipe
On change la règle
ArchUnit
Après avoir tout refactoré...
Pour la consultation du status
On a choisi de conserver cette méthode au niveau du
DomainEn construisant le
statusbasé sur lestreamde l'aggrégat
On aurait pu, depuis le
Use Casepassé par une projection
On va pouvoir "cleaner" notre
PartieDeChassePlus besoin de gérer les
mythoevents au sein de l'aggrégat avec la méthodeEmitEventLa gestion du temps est complètement faites via le
TimeProviderfournit en entréeOn a plus besoin de passer de référence dans nos méthodes
Concernant le test utilisant le mécanisme d'
Approvalsur le démarrage d'une partieOn effectue l'approbation, non plus sur l'aggrégat, mais sur le dernier event émis
Revue de l'encapsulation
Au niveau de la partie de chasse on expose quelques propriétés mais uniquement pour des besoins de testing

On doit se poser la question :
Sommes nous assez confiant en testant uniquement que les events sont bien raised par notre PartieDeChasse?
Pour moi, on peut se dire que oui :
On a couvert les transitions qui, intrinsèquement, vont valider l'application des events
On encapsule les fields et corrigent les tests
On peut
splitterla classePartieDeChassepar comportement en utilisant despartialclassesN'a absolument aucun impact sur les consommateurs mais peut être plus facile à comprendre / maintenir

Exemple de
partialclass :
Nouveau rapport Sonar Cloud
Sonar CloudLe rapport est disponible ici.
L'outil identifie un problème avec les méthodes
ApplyElles sont appelées uniquement via réflexion

Si on veut
by-passercette règle on peut utiliser une des stratégies définies avec la règlePlus d'informations ici
On choisit de flagger avec 1 attribut les méthodes
Apply
On en profite pour changer la manière de récupérer les méthodes
Applydans leConventionEventRouter
CodeScene
Après tous ces refactorings on observe la santé du code via codescene :

Félicitations sa santé est en nette amélioration 🎉🎉🎉
Reflect
Qu'est-ce que cela a simplifié ?
Au contraire complexifié ?
Qu'est ce que tu en penses ?
Qu'est-ce que tu changerais ?

Last updated
Was this helpful?