Ce document détaille foodtruacker, un projet mettant en œuvre la conception pilotée par domaine (DDD), CQRS et Event Sourcing. Il utilise ASP.NET Core et se concentre sur l'amélioration de la maintenabilité dans des domaines métier complexes. Le projet utilise une analyse de rentabilisation fictive simplifiée à des fins d’illustration. Cette explication détaillée couvre les motivations, les fonctionnalités, les détails de mise en œuvre et les technologies pertinentes.
foodtruacker - Implémentation de DDD, CQRS et Event Sourcing
Ce projet événementiel utilise des principes, des cadres et des architectures, tous centrés sur l'idée d'améliorer la maintenabilité lorsqu'il s'agit de systèmes reflétant des domaines commerciaux complexes. L'API Web de l'application s'appuie sur le framework ASP.NET Core de Microsoft et implémente la conception basée sur le domaine, ainsi que les modèles CQRS et Event Sourcing. Un business case fictif pose les bases de ce projet et est le résultat d'un atelier de prise d'assaut d'événements.
Remarque : le domaine commercial fictif présenté dans ce projet est fortement simplifié et ne doit être considéré que comme un fournisseur de cas d'utilisation pertinents.
Motivation
Puisqu'il n'est pas toujours préférable d'utiliser des opérations CRUD et des objets POCO dans des projets avec des domaines métier plutôt complexes, j'ai décidé de créer ce projet comme une mise en œuvre pratique de mes recherches sur - et de mon intérêt pour - la conception pilotée par domaine (DDD). approche du développement de logiciels.
Étant donné que le business case fictif présenté dans ce projet est fortement événementiel, j'ai décidé de mettre également en œuvre les modèles CQRS et Event Sourcing. Les deux ont attiré mon attention lors des recherches pour ce projet et vont bien avec DDD.
Caractéristiques
Aperçu
Ce projet se compose d'une application API Web exécutable et de plusieurs composants fonctionnels, chacun fourni via des bibliothèques de classes. Le code est organisé par espaces de noms. Dans les applications ASP.NET Core créées dans Visual Studio, les espaces de noms, par défaut, sont automatiquement créés à partir de la structure des dossiers des projets. Consultez le diagramme ci-dessous pour un aperçu de la structure des dossiers (et de l'espace de noms) de ce projet :
Introduction
Prise d'assaut d'événements
Un format d'atelier flexible pour l'exploration collaborative de domaines commerciaux complexes, inventé par Alberto Brandolini. Il s'agit d'une méthodologie extrêmement légère pour améliorer, visualiser, explorer et concevoir rapidement les flux et processus commerciaux au sein de votre organisation.
L'atelier se compose d'un groupe de personnes ayant des expertises différentes utilisant des notes autocollantes colorées pour mettre en forme de manière collaborative les processus métier pertinents. Il est obligatoire pour un atelier EventStorming d'avoir à la fois les bonnes personnes présentes et de disposer de suffisamment de surface pour y placer les post-it. Les personnes requises incluent généralement celles qui connaissent les questions à poser (généralement les développeurs) et celles qui connaissent les réponses (experts du domaine, propriétaires de produits).
L'objectif de cet atelier est de permettre aux participants d'apprendre les uns des autres, de révéler et de réfuter les idées fausses et, par exemple dans ce projet GitHub, de jeter les bases du développement d'une solution logicielle basée sur des événements reflétant un domaine d'activité corrélé.
Conception pilotée par domaine (DDD)
Une approche du développement logiciel qui centre le développement sur la programmation d'un modèle de domaine doté d'une riche compréhension des processus et des règles d'un domaine métier corrélé. Le terme « Domain-driven Design » a été inventé par Eric Evans dans son livre du même titre.
DDD vise à faciliter la création d'applications complexes et se concentre sur trois principes fondamentaux :
Le livre d'Eric Evans définit quelques termes courants pour le Domain-Driven Design :
Modèle de domaine
Un système d'abstractions qui décrit les processus et les politiques d'un domaine métier et est utilisé pour gérer les tâches requises associées à ce domaine.
Langue omniprésente
Mots et déclarations pour certains éléments du domaine commercial. Afin d’éviter les idées fausses, tous les membres de l’équipe doivent adopter certains termes, généralement ceux utilisés par les experts du domaine.
Contexte délimité
Une limite conceptuelle à l'intérieur de laquelle un modèle de domaine particulier est défini et applicable. Cela représente généralement un sous-système ou un domaine de travail. Il s'agit principalement d'une délimitation linguistique, chaque contexte délimité ayant sa propre langue omniprésente.
Par exemple : Gestion des clients où un utilisateur est appelé « client ».
Le livre d'Eric Evans différencie davantage certaines parties du modèle de domaine. Pour n'en nommer que quelques-uns :
Entité
Un objet qui se définit par son identité plutôt que par ses attributs.
Ex : Une personne restera toujours la même personne, peu importe le choix de la veste, la couleur des cheveux ou la langue parlée à un moment donné.
Objet de valeur
Un objet qui est défini uniquement par la valeur de ses attributs. Les objets de valeur sont immuables et n'ont pas d'identité unique. Les objets de valeur peuvent être remplacés par d'autres objets de valeur ayant les mêmes attributs.
Par exemple : lorsque vous vous concentrez sur une personne, une paire de lunettes de soleil cassée peut facilement être remplacée par une nouvelle paire de lunettes de soleil tout aussi esthétique.
Agrégat
Un cluster d'une ou plusieurs entités et objets de valeur facultatifs, unifiés pour constituer une seule unité transactionnelle. Une entité formera la base de l'agrégat et est ainsi déclarée racine de l'agrégat. Toutes les propriétés de ses entités collaboratrices et de ses objets de valeur ne peuvent être accessibles que via cette entité de base unique. Un agrégat doit toujours être dans un état cohérent. Dans la programmation orientée objet, cela se fait généralement en utilisant des setters privés et des getters protégés.
Par exemple : dans un contexte de vente de voitures, une voiture (entité) est définie par son numéro d'identification de véhicule. Cette voiture pourrait avoir quatre roues (objets de valeur), qui pourraient devoir être remplacées après un certain temps.
Événement de domaine
Objet créé à la suite d'une activité au sein du modèle de domaine. Il est utilisé pour conserver et transmettre des informations liées à cette activité. Les événements de domaine sont généralement créés pour les activités que les experts du domaine considèrent comme pertinentes.
Architecture hexagonale (Ports et adaptateurs)
Modèle d'architecture utilisé dans la conception de logiciels, proposé par Alistair Cockburn en 2005. Le modèle vise à atteindre un haut degré de maintenabilité et décrit une application en trois couches. Chaque couche communique avec la ou les couches adjacentes à l'aide d'interfaces (ports) et d'implémentations (adaptateurs) :
La règle clé de ce modèle d’architecture est que les dépendances ne peuvent pointer que vers l’intérieur. Rien dans un cercle intérieur ne peut rien savoir de quelque chose dans un cercle extérieur. Toutes les dépendances souhaitant pointer vers l'extérieur, par exemple en appelant une base de données depuis la couche application, doivent être instanciées via une inversion de contrôle (IoC) ou une injection de dépendances (DI).
CQRS utilisant MediatR (un framework de messagerie prédéfini)
CQRS signifie Command/Query Responsibility Segregation et a été décrit pour la première fois par Greg Young en 2010. Il est basé sur le principe de Command Query Separation (CQS) et permet de séparer les opérations de lecture et d'écriture. CQS déclare :
L'amélioration de CQRS par rapport à CQS réside dans le fait que ces commandes et requêtes sont traitées comme des modèles plutôt que comme des méthodes. Ces modèles peuvent être distribués sous forme d'objets à un moment donné, pour ensuite être gérés par leurs gestionnaires respectifs requis à un autre point du système, chacun renvoyant ses modèles de réponse pour une ségrégation claire de chaque action.
Le modèle médiateur permet d'implémenter des commandes/requêtes et des gestionnaires faiblement couplés, en utilisant un objet médiateur. Les objets ne communiquent plus directement entre eux, mais via le médiateur.
Le framework MediatR est une implémentation open source du modèle médiateur, créé par Jimmy Bogard. Il sera utilisé dans ce projet pour la communication entre la couche framework et la couche application. Il sera également utilisé pour projeter les données de la base de données Command vers la base de données Query.
Recherche d'événements
Un modèle de conception architecturale permettant de stocker chaque changement dans l'état d'une application, plutôt que de stocker uniquement l'état actuel des données dans un domaine. Ce modèle a été introduit par Greg Young et a depuis connu de nombreuses adoptions.
Le modèle vise à capturer chaque modification de l'état d'une application en tant qu'objet événement. Ces objets d'événement sont ensuite stockés, dans la séquence d'occurrence, de manière à ajouter uniquement. Cela permet non seulement de recréer l'état actuel d'un objet sur la séquence d'événements qui se sont produits jusqu'à présent, mais permet finalement de remonter dans le temps et de recréer l'état de l'objet à un moment donné.
Un compte bancaire peut être un bon exemple du principe d’Event Sourcing. Chaque fois que de l’argent est retiré ou déposé, au lieu de simplement mettre à jour le solde actuel, le montant de la monnaie est enregistré. Le solde actuel est ensuite calculé en parcourant la séquence des événements, avec les informations correspondantes sur le montant d'argent retiré ou déposé à chaque fois.
Le sourcing d'événements fonctionne bien avec la conception basée sur le domaine, car il convient parfaitement au stockage des événements de domaine, déclenchés par le modèle de domaine à chaque demande de modification.
Event Sourcing bénéficie également grandement du CQRS. Au lieu de devoir effectuer une requête sur la base de données Event Sourcing, qui devrait parcourir tous les événements enregistrés liés à l'objet demandé afin de recréer l'état actuel, cette requête peut être effectuée sur une base de données de requête dédiée. Cette base de données de requêtes est mise à jour par ses propres gestionnaires d'événements, écoutant les mêmes événements distribués juste après avoir été ajoutés à la base de données de sourcing d'événements. Ces processus de mise à jour sont appelés Projections.
Cette séparation des bases de données ouvre également la voie à un énorme potentiel d’évolutivité et d’optimisation des performances. Plusieurs instances de la base de données Query peuvent être créées et synchronisées simplement en faisant en sorte que leurs gestionnaires d'événements écoutent les événements distribués à partir du client de base de données Event Sourcing juste après qu'une modification pertinente de l'état d'une application se soit produite. Le choix du type de base de données ainsi que le degré de dénormalisation des données, optimisé par requête, peuvent grandement améliorer les performances.
Cette mise à jour constante du modèle de lecture peut se produire de manière synchrone ou asynchrone. Cette dernière solution se fait au détriment d'une éventuelle cohérence, le modèle de lecture étant désynchronisé avec le modèle d'écriture pendant un infime intervalle de temps (généralement quelques millisecondes).
Noyau partagé
Une bibliothèque commune pour la couche de domaine, qui contient des classes de base communes spécifiques à la conception axée sur le domaine, des entités de domaine, des objets de valeur, etc. qui sont partagés dans des contextes délimités.
Commencer
Pour lancer ce projet tel quel, n'hésitez pas à suivre ces étapes :
Conditions préalables
Installation
Lancez https://localhost:5001/swagger/index.html dans votre navigateur pour afficher la documentation Swagger de votre API.
Utilisez Swagger, Postman ou toute autre application pour envoyer une requête POST à https://localhost:5001/api/Administration/Register pour enregistrer votre compte administrateur initial. Envoyez l'objet suivant :
Examinez l'application console ou la sortie reconfigurée pour les journaux de l'application. Après toute inscription réussie d'un utilisateur, un lien de vérification par courrier électronique - fourni par EmailService - doit être écrit dans les journaux. Copiez et collez cette URL dans votre navigateur et appuyez sur Entrée pour terminer l'inscription. N'hésitez pas à modifier ou à développer cette mauvaise implémentation d'un service de messagerie ;-)
Vous êtes prêt. Connectez-vous ensuite.
Lancez http://localhost:2113/ dans votre navigateur pour afficher l'interface graphique EventStoreDB. Ouvrez l'onglet "Stream Browser" pour voir tous les événements stockés.
Les tests peuvent être exécutés en exécutant :
Technologies
Ce projet utilise les packages Technologies/NuGet suivants :
Ressources / Lectures recommandées
Alberto Brandolini :
https://www.eventstorming.com
Vaughn Vernon :
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_1.pdf
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_2.pdf
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_3.pdf
Alistair Cockburn :
https://web.archive.org/web/20180822100852/http://alistair.cockburn.us/Hexagonal+architecture
Robert C. Martin (oncle Bob) :
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
César de la Torre, Bill Wagner, Mike Rousos :
https://docs.microsoft.com/en-us/dotnet/architecture/microservices/
Greg Young
https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf
https://cqrs.wordpress.com/documents/building-event-storage/
https://msdn.microsoft.com/en-us/library/jj591559.aspx
Martin Fowler :
https://www.martinfowler.com/bliki/CQRS.html
Jimmy Bogard :
https://github.com/jbogard/MediatR
https://www.youtube.com/watch?v=SUiWfhAhgQw
Conception basée sur le domaine :
https://dddcommunity.org
https://thedomaindrivendesign.io
https://dotnetcodr.com/2013/09/12/a-model-net-web-service-based-on-domain-driven-design-part-1-introduction/
https://dotnetcodr.com/2015/10/22/domain-driven-design-with-web-api-extensions-part-1-notifications/
Architecture hexagonale :
https://fideloper.com/hexagonal-architecture
https://herbertograca.com/2017/09/14/ports-adapters-architecture/
Crédits
http://www.andreavallotti.tech/en/2018/01/event-sourcing-and-cqrs-in-c/
https://www.exceptionnotfound.net/real-world-cqrs-es-with-asp-net-and-redis-part-1-overview/
https://buildplease.com/pages/fpc-1/
https://dotnetcoretutorials.com/2019/04/30/the-mediator-pattern-in-net-core-part-1-whats-a-mediator/
https://itnext.io/why-and-how-i-implemented-cqrs-and-mediator-patterns-in-a-microservice-b07034592b6d