12) "Event Sourcing"

Step 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 Sourcing

  • Quelques classes ont déjà été implémenté afin de faciliter l'utilisation d'1 Event Store in memory

    • Faire 1 checkout du commit 6efde7c3e470e7c84c50da2715c255bd9acd3d6c

  • 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.Core

    • Ce code est fortement inspiré du travail fait sur NEventStore

    • Pour 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

Faire en sorte que le flux ressemble à cela :

Command / Event(s)
  • Pour le moment au sein de notre Domain son implémentation ressemble à ça :

  • Soyons plus explicite en retournant Either<Error, Unit>

    • On ne stockera plus l'état mais que les Events donc 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

First red test
  • On génère l'Event "structuré" depuis le test

    • On choisi d'utiliser 1 record parce qu'ils sont immuables par design

    • On ajoute la référence sur le projet Domain.Core

  • On doit maintenant ajouter 1 overload sur notre class PartieDeChasseAssertions afin 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 EventStore

    • On va donc vérifier que dans le stream d'events associé à l'instance de notre aggrégat notre event est bien présent

    • On ajoute alors dans l'assertion 1 dépendance sur le repository

  • Pour continuer on doit itérer sur notre interface de repository

    • On commence à inclure de l'asynchronisme en utilisant 1 OptionAsync (toujours de LanguageExt)

    • On adapte aussi l'assertion (on va utiliser l'AsyncHelper pour se faciliter la tâche)

  • On ajoute 1 instance d'EventStore au sein du PartieDeChasseRepositoryForTests

    • Sachant que pour les besoins de testing nous utiliserons l'instance InMemory

    • Ce 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

PartieDeChasse n'est pas 1 Aggregate
  • On lui fait hériter de Aggregate et 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

Fail car aucun événement trouvé
Fail ca aucun événement trouvé
  • 🟢 On raise l'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 Domain afin de grouper ensemble les couples Command | Event

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

Broken Architecture Rule
  • On va alors créer 1 ADR (Architecture Decision Record) pour expliquer pourquoi on a voulu dévier de cette règle

    • Plus 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 Domain

      • En construisant le status basé sur le stream de l'aggrégat

    • On aurait pu, depuis le Use Case passé par une projection

  • On va pouvoir "cleaner" notre PartieDeChasse

    • Plus besoin de gérer les mytho events au sein de l'aggrégat avec la méthode EmitEvent

    • La gestion du temps est complètement faites via le TimeProvider fournit en entrée

      • On a plus besoin de passer de référence dans nos méthodes

  • Concernant le test utilisant le mécanisme d'Approval sur le démarrage d'une partie

    • On 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

public methods sur PartieDeChasse
  • 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 splitter la classe PartieDeChasse par comportement en utilisant des partial classes

    • N'a absolument aucun impact sur les consommateurs mais peut être plus facile à comprendre / maintenir

Partial classes
  • Exemple de partial class :

Nouveau rapport Sonar Cloud

Le rapport est disponible ici.

  • L'outil identifie un problème avec les méthodes Apply

    • Elles sont appelées uniquement via réflexion

Major issues SonarCloud
  • Si on veut by-passer cette règle on peut utiliser une des stratégies définies avec la règle

    • Plus 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 Apply dans le ConventionEventRouter

CodeScene

Après tous ces refactorings on observe la santé du code via codescene :

Codescene debriefe

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?