Partager via


Concevoir un microservice orienté DDD

Conseil / Astuce

Ce contenu est un extrait du livre électronique 'Architecture des microservices .NET pour les applications .NET conteneurisées', disponible sur .NET Docs ou en tant que PDF téléchargeable gratuitement, lisible hors ligne.

Architecture de microservices .NET pour les applications .NET conteneurisées - vignette de couverture du livre électronique.

La conception pilotée par le domaine (DDD) préconise la modélisation basée sur la réalité de l’entreprise en fonction de vos cas d’usage. Dans le contexte de la création d’applications, DDD parle des problèmes en tant que domaines. Il décrit des zones de problème indépendantes en tant que contextes délimités (chaque contexte délimité est corrélé à un microservice) et met l’accent sur un langage commun pour parler de ces problèmes. Il suggère également de nombreux concepts et modèles techniques, comme les entités de domaine avec des modèles enrichis (aucun modèle de domaine anémique), des objets de valeur, des agrégats et des règles racine d’agrégation (ou entité racine) pour prendre en charge l’implémentation interne. Cette section présente la conception et l’implémentation de ces modèles internes.

Parfois, ces règles et modèles techniques DDD sont perçus comme des obstacles qui ont une courbe d’apprentissage abrupte pour implémenter des approches DDD. Mais la partie importante n’est pas les modèles eux-mêmes, mais l’organisation du code afin qu’elle soit alignée sur les problèmes métier et utilise les mêmes termes métier (langage omniprésent). En outre, les approches DDD ne doivent être appliquées que si vous implémentez des microservices complexes avec des règles métier importantes. Des responsabilités plus simples, comme un service CRUD, peuvent être gérées avec des approches plus simples.

Où dessiner les limites est la tâche clé lors de la conception et de la définition d’un microservice. Les modèles DDD vous aident à comprendre la complexité du domaine. Pour le modèle de domaine pour chaque contexte délimité, vous identifiez et définissez les entités, les objets de valeur et les agrégats qui modélisent votre domaine. Vous générez et affinez un modèle de domaine contenu dans une limite qui définit votre contexte. Et c’est explicite sous la forme d’un microservice. Les composants au sein de ces limites finissent par être vos microservices bien que, dans certains cas, un contexte délimité ou des microservices métier peuvent être composés de plusieurs services physiques. DDD concerne les limites et les microservices.

Garder les frontières du contexte des microservices relativement réduites.

Déterminer où placer des limites entre les contextes délimités équilibre deux objectifs concurrents. Tout d’abord, vous souhaitez créer initialement les plus petits microservices possibles, bien que ce ne soit pas le pilote principal ; vous devez créer une limite autour des choses qui ont besoin de cohésion. Deuxièmement, vous souhaitez éviter la communication verbeuse entre les microservices. Ces objectifs peuvent contredire les uns les autres. Vous devez les équilibrer en décomposant le système en autant de microservices que vous pouvez jusqu’à ce que vous voyiez les limites de communication croître rapidement avec chaque tentative supplémentaire de séparer un nouveau contexte délimité. La cohésion est essentielle dans un contexte limité unique.

Elle est similaire aux mauvaises odeurs (ou « code smell ») lors de l’implémentation de classes. Si deux microservices doivent collaborer beaucoup entre eux, ils doivent probablement être le même microservice.

Une autre façon de regarder cet aspect est l’autonomie. Si un microservice doit s’appuyer sur un autre service pour traiter directement une demande, il n’est pas vraiment autonome.

Couches dans les microservices DDD

La plupart des applications d’entreprise présentant une complexité métier et technique significative sont définies par plusieurs couches. Les couches sont un artefact logique et ne sont pas liées au déploiement du service. Ils existent pour aider les développeurs à gérer la complexité dans le code. Différentes couches (comme la couche de modèle de domaine par rapport à la couche de présentation, etc.) peuvent avoir différents types, qui imposent des traductions entre ces types.

Par exemple, une entité peut être chargée à partir de la base de données. Ensuite, une partie de ces informations, ou une agrégation d’informations, y compris des données supplémentaires provenant d’autres entités, peuvent être envoyées à l’interface utilisateur du client via une API web REST. Le point ici est que l’entité de domaine est contenue dans la couche de modèle de domaine et ne doit pas être propagée à d’autres zones auxquelles elle n’appartient pas, comme à la couche de présentation.

En outre, vous devez disposer d’entités toujours valides (consultez la section Conception des validations dans la section couche de modèle de domaine ) contrôlée par les racines d’agrégation (entités racines). Par conséquent, les entités ne doivent pas être liées aux vues clientes, car au niveau de l’interface utilisateur, certaines données peuvent ne pas encore être validées. C’est pour cette raison que le ViewModel est destiné. ViewModel est un modèle de données exclusivement pour les besoins de la couche de présentation. Les entités de domaine n’appartiennent pas directement à ViewModel. Au lieu de cela, vous devez traduire entre ViewModels et les entités de domaine et vice versa.

Lors de la résolution de la complexité, il est important d’avoir un modèle de domaine contrôlé par des racines d’agrégation qui vérifient que tous les invariants et règles liés à ce groupe d’entités (agrégat) sont effectuées par le biais d’un point d’entrée ou d’une porte unique, la racine d’agrégation.

La figure 7-5 montre comment une conception en couches est implémentée dans l’application eShopOnContainers.

Diagramme montrant les couches d’un microservice de conception piloté par un domaine.

Figure 7-5. Couches DDD dans le microservice de commandes dans eShopOnContainers

Les trois couches d’un microservice DDD comme Ordering. Chaque couche est un projet VS : la couche Application est Ordering.API, la couche Domaine est Ordering.Domain et la couche Infrastructure est Ordering.Infrastructure. Vous souhaitez concevoir le système afin que chaque couche communique uniquement avec certaines autres couches. Cette approche peut être plus facile à appliquer si des couches sont implémentées en tant que bibliothèques de classes différentes, car vous pouvez clairement identifier les dépendances définies entre les bibliothèques. Par exemple, la couche de modèle de domaine ne doit pas dépendre d’une autre couche (les classes de modèle de domaine doivent être des objets de classe anciens bruts ou poCO). Comme illustré dans la figure 7-6, la bibliothèque de couche Ordering.Domain a des dépendances uniquement sur les bibliothèques .NET ou les packages NuGet, mais pas sur une autre bibliothèque personnalisée, telle que la bibliothèque de données ou la bibliothèque de persistance.

Capture d’écran des dépendances Ordering.Domain.

Figure 7-6. Les couches implémentées en tant que bibliothèques permettent un meilleur contrôle des dépendances entre les couches

Couche de modèle de domaine

L’excellent livre d’Eric Evans Domain Driven Design indique ce qui suit sur la couche de modèle de domaine et la couche application.

Couche modèle de domaine : responsable de la représentation des concepts de l’entreprise, des informations sur la situation métier et les règles métier. L’état qui reflète la situation de l’entreprise est contrôlé et utilisé ici, même si les détails techniques du stockage sont délégués à l’infrastructure. Cette couche est le cœur du logiciel métier.

La couche de modèle de domaine est l’emplacement où l’entreprise est exprimée. Lorsque vous implémentez une couche de modèle de domaine de microservice dans .NET, cette couche est codée en tant que bibliothèque de classes avec les entités de domaine qui capturent des données plus le comportement (méthodes avec logique).

En suivant les principes d’ignorance de persistance et d’ignorance de l’infrastructure , cette couche doit ignorer complètement les détails de persistance des données. Ces tâches de persistance doivent être effectuées par la couche d’infrastructure. Par conséquent, cette couche ne doit pas prendre de dépendances directes sur l’infrastructure, ce qui signifie qu’une règle importante est que vos classes d’entités de modèle de domaine doivent être des POCO.

Les entités de domaine ne doivent pas avoir de dépendance directe (comme la dérivation à partir d’une classe de base) sur n’importe quelle infrastructure d’accès aux données, comme Entity Framework ou NHibernate. Dans l'idéal, vos entités de domaine ne devraient pas dériver ou implémenter un type défini dans aucun cadre d'infrastructure.

La plupart des frameworks ORM modernes comme Entity Framework Core permettent cette approche, afin que vos classes de modèle de domaine ne soient pas couplées à l’infrastructure. Toutefois, l’utilisation d’entités POCO n’est pas toujours possible lors de l’utilisation de certaines bases de données et infrastructures NoSQL, telles que Actors et Reliable Collections dans Azure Service Fabric.

Même s’il est important de suivre le principe d’ignorance de persistance pour votre modèle de domaine, vous ne devez pas ignorer les problèmes de persistance. Il est toujours important de comprendre le modèle de données physiques et la façon dont il est mappé à votre modèle objet d’entité. Sinon, vous pouvez créer des conceptions impossibles.

De plus, cet aspect ne signifie pas que vous pouvez prendre un modèle conçu pour une base de données relationnelle et le déplacer directement vers une base de données Orientée Document ou NoSQL. Dans certains modèles d’entité, le modèle peut s’adapter, mais il ne le fait généralement pas. Il existe toujours des contraintes auxquelles votre modèle d’entité doit adhérer, en fonction de la technologie de stockage et de la technologie ORM.

Couche d’application

En passant à la couche application, nous pouvons à nouveau citer le livre d’Eric Evans Domain Driven Design :

Couche Application : Définit les travaux que le logiciel est censé faire et diriger les objets de domaine expressifs pour résoudre les problèmes. Les tâches dont cette couche est responsable sont significatives pour l’entreprise ou nécessaires à l’interaction avec les couches d’application d’autres systèmes. Cette couche est maintenue mince. Elle ne contient pas de règles de gestion ou de connaissances, mais coordonne uniquement les tâches et délègue le travail à des collaborations d'objets de domaine dans la couche suivante. Il n’a pas d’état reflétant la situation métier, mais il peut avoir l’état qui reflète la progression d’une tâche pour l’utilisateur ou le programme.

La couche d’application d’un microservice dans .NET est généralement codée en tant que projet d’API web core ASP.NET. Le projet implémente l'interaction du microservice, l'accès au réseau à distance et les API Web externes utilisées à partir de l'interface utilisateur ou des applications clientes. Il inclut des requêtes si vous utilisez une approche CQRS, les commandes acceptées par le microservice et même la communication pilotée par les événements entre les microservices (événements d’intégration). L’API web ASP.NET Core qui représente la couche Application ne doit pas contenir de règles d’entreprise ou de connaissances de domaine (en particulier des règles de domaine pour les transactions ou les mises à jour) ; celles-ci doivent être détenues par la bibliothèque de classes de modèle de domaine. La couche Application doit uniquement coordonner les tâches et ne doit pas contenir ni définir d’état de domaine (modèle de domaine). Il délègue l’exécution de règles métier aux classes de modèle de domaine elles-mêmes (agréger les racines et les entités de domaine), ce qui met à jour les données au sein de ces entités de domaine.

En fait, la logique d’application est l’endroit où vous implémentez tous les cas d’usage qui dépendent d’un serveur frontal donné. Par exemple, l’implémentation liée à un service d’API web.

L’objectif est que la logique de domaine dans la couche de modèle de domaine, ses invariants, le modèle de données et les règles métier associées doivent être complètement indépendantes des couches de présentation et d’application. La plupart du temps, la couche de modèle de domaine ne doit pas dépendre directement d’une infrastructure d’infrastructure.

Couche d’infrastructure

La couche d’infrastructure est la façon dont les données initialement conservées dans les entités de domaine (en mémoire) sont conservées dans des bases de données ou dans un autre magasin persistant. Par exemple, l’utilisation du code Entity Framework Core pour implémenter les classes de modèle référentiel qui utilisent un DBContext pour conserver des données dans une base de données relationnelle.

Conformément aux principes d'Ignorance de la Persistance et d'Ignorance de l'Infrastructure mentionnés précédemment, la couche d'infrastructure ne doit pas "contaminer" la couche du modèle de domaine. Vous devez conserver les classes d’entité de modèle de domaine indépendantes de l’infrastructure que vous utilisez pour conserver des données (EF ou tout autre framework) en ne prenant pas de dépendances difficiles sur les frameworks. Votre bibliothèque de classes de couche de modèle de domaine doit avoir uniquement votre code de domaine, mais seulement les classes d’entité POCO implémentant le cœur de votre logiciel et complètement découplées des technologies d’infrastructure.

Ainsi, vos couches ou bibliothèques de classes et projets doivent en fin de compte dépendre de votre couche de modèle de domaine (bibliothèque), et non inversement, comme illustré dans la figure 7-7.

Diagramme montrant les dépendances qui existent entre les couches de service DDD.

Figure 7-7. Dépendances entre les couches dans DDD

Les dépendances dans un service DDD, la couche Application dépend du domaine et de l’infrastructure, et l’infrastructure dépend du domaine, mais le domaine ne dépend d’aucune couche. Cette conception de couche doit être indépendante pour chaque microservice. Comme indiqué précédemment, vous pouvez implémenter les microservices les plus complexes en suivant les modèles DDD, tout en implémentant des microservices plus simples pilotés par les données (CRUD simple dans une seule couche) d’une manière plus simple.

Ressources supplémentaires