Applications distribuées en .NET

Première implémentation avec CQRS et l'Event Sourcing

Clément Bouillier - Devcrafting - @clem_bouillier

Un peu de contexte par rapport à CQRS et l'Event Sourcing

Rappel : CQRS conceptuel

Conceptual CQRS schema : UX, Command and Query representation with main concepts involved

Rappel : CQRS conceptuel

  1. L'utilisateur interagit avec l'application et le monde réel, pour prendre des décisions
  2. La partie Command traite les décisions demandées par l'utilisateur
  3. La partie Query permet d'afficher à l'utilisateur les données lui permettant de prendre d'autres décisions
  4. Les événements métier sont les pivots entre Command et Query

Pourquoi CQRS et Event Sourcing ?

  1. Beaucoup plus de READ que de WRITE donc autant séparer les modèles
  2. Stocker les événements modifiant le système plutôt que son état actuel : le modèle est plus riche en information métier
  3. "Complexité accidentelle" limitée : pas de modèle unique (+ORM, BD unique...), moins de besoins frameworks
  4. Code plus simple, plus découplé, plus testable et plus orienté micro-services

Comparaison schématique d'une architecture N-tiers classique et d'une architecture orientée CQRS/Event Sourcing

Schema with typical N-tier architecture compared to CQRS style architecture Pour aller plus loin: très bonne présentation de différents styles d'architecture (dont N-tiers et CQRS)

Quelques pistes avant de commencer

Nos amis pour cette session

  1. La MSDN sur C# : https://msdn.microsoft.com/en-us/library/67ef8sbd.aspx
  2. Mais aussi votre moteur de recherche préféré
  3. Object Browser dans Visual Studio

Quelques contraintes pour s'initier au TDD/TDL et au Pair Programming

  1. Pair Programming : les 2 personnes sont actives, le clavier DOIT être échangé
  2. Quand on n'a pas le clavier, il faut guider l'autre, discuter...pas attendre qu'il ait fini
  3. TDD/TDL : cycle red->green->refactor
  4. On utilise tous la même norme de code (via StyleCop + R# ?)

En pratique pour ce TD

  1. Repository GitHub : git clone https://github.com/devcrafting/Distributed.NETTraining.git (ou fork + clone)
  2. Un tag par étape, le tag de l'étape en cours sera affiché dans ce support
  3. Pour chaque tag, de nouvelles classes et de nouveaux tests à décommenter au fur et à mesure
  4. Pour les tests, on utilise NUnit et NFluent (récupérés via NuGet)

Ce que contient la solution fournie

  1. Une librairie pour le contexte Messaging de l'application Twitter-like modélisée en Event Storming
  2. Une libraire de test pour Messaging
  3. Dans chaque librairie, une séparation Domain (métier)/Infrastructure (technique)
  4. Une API HTTP (REST?!) faites avec Nancy FX, à requêter avec un client rest (plugins Advanced REST sur Chrome ou RESTClient sur Firefox)

Itération 1 : implémenter un read model permettant d'afficher une liste de tweets

Pattern Repository

  1. Objet représentant une liste d'objets/structures
  2. Avec des méthodes de manipulations du type GetByXXX, GetAll, Save
  3. Abstraction courante d'une entité/d'un groupe d'entité SQL ou d'une collection MongoDB par exemple
  4. Proche du pattern DAO, mais différent (moins proche de la base, plus proche du domaine métier)

Injection de dépendance/inversion de contrôle (DI/Ioc)

  1. Un concept sous-jacent : Dependency Inversion (le D de l'acronyme SOLID)
  2. Externaliser l'instanciation des dépendances d'un objet et les abstraire avec une interface (l'implémentation doit être un détail)
  3. Enregistrer les implémentations avec leurs abstractions
  4. Avec Nancy : la plupart des engistrements sont faits automatiquement sur convention de nommage (Super Happy Dappy Path)

Nancy FX

  1. Une route (i.e URL) = une fonction (ou lambda)
  2. Le type du retour est "négocié" en fonction du header Accept de la requête
  3. Il suffit donc de retourner les objets récupérer dans le repository
  4. Pour obtenir du JSON, il faudra utiliser "application/json" dans le client REST

Immutabilité

  1. Propriété d'un objet de ne pas pouvoir être modifié
  2. Réduit les risques d'effet de bord (très courant en programmation fonctionnelle)
  3. "Simulation" en objet avec des constructeurs avec paramètres, des attributs "readonly" et PAS DE SETTER

Affichage de la timeline -> tag TD_ReadModel_TimelineMessage

Nous avons besoin des éléments suivants :
  1. Une structure TimelineMessage (read model) - TimelineMessageTests
  2. Une interface du Repository de ces structures - TimelineMessageRepositoryTests
  3. Une implémentation du Repository avec stockage en mémoire (champs privé IEnumerable<TimelineMessage>)
  4. Utiliser ce repository dans le module Nancy pour la route "/messaging/timelineMessages" - MessagingModule

Retrospective

  1. Qu'est-ce qui c'est bien passé ?
  2. Quels ont été les points difficiles ?
  3. Qu'est-ce que vous avez appris ?
  4. Quels sont les points qui vous perturbent encore ?

Itération 2 : projections permettant d'obtenir les read models à partir d'événements

Evénements ayant un impact sur le read model TimelineMessage

  1. MessagePublished
  2. FollowerMessagePublished
  3. FollowerMessageRepublished
  4. FollowerMessageLiked
  5. MessageDeleted
  6. ...

Projections -> tag TD_Projections_TimelineMessage

  1. Une projection est classe traitant un événément à la fois pour transformer un read model à la fois (séparation des responsabilités)
  2. Signature typique de la méthode de traitement : void Handle([TypeEvent] @event)
  3. On peut conserver l'immutabilité avec une surcharge des constructeurs

Retrospective

  1. Qu'est-ce qui c'est bien passé ?
  2. Quels ont été les points difficiles ?
  3. Qu'est-ce que vous avez appris ?
  4. Quels sont les points qui vous perturbent encore ?

Itération 3 : agrégats levant des événements en conséquence de commandes

Agrégat racine (AggregateRoot)

  1. Objet garantissant la consistance des changements réalisés en son sein => encapsulation nécessaire (i.e pas de setters, ni de getters)
  2. Conserve seulement dans un état interne ce dont il a besoin pour prendre des décisions ultérieures
  3. L'état est construit à partir des événements que l'agrégat lève lui-même (ET UNIQUEMENT par ce biais)
  4. Cela permet de reconstruire un agrégat à partir de l'historique des événements qui aura été stocké (Event Sourcing)

Pattern Publish/Subscribe

  1. Objet gérant la diffusion des événements
  2. Différentes utilisations pour les Events : passé en paramètre des méthodes de l'agrégat, conserver une liste des Events non "committés" + Repository.Save, retourner la liste des Events générés par les méthodes de l'agrégat

Agrégat Message et les commandes associées -> tag TD_Agregat_Message

  1. Utiliser la technique du passage en paramère de l'EventPublisher (pattern Publish/Subscribe), avec une interface
  2. L'agrégat Message va assurer qu'un message n'est pas republié par son auteur
  3. Mais également qu'un message ne peut pas être republié 2 fois par le même compte
  4. Ou encore que la suppression ne peut se faire que par l'auteur (/!\ une suppression "administrative" serait gérée par une autre commande)

Retrospective

  1. Qu'est-ce qui c'est bien passé ?
  2. Quels ont été les points difficiles ?
  3. Qu'est-ce que vous avez appris ?
  4. Quels sont les points qui vous perturbent encore ?