Dans le précédent article [6], nous avons commencé à explorer l’Odyssée du Domain Driven Design (DDD), tout en nous équipant des outils nécessaires. Maintenant, nous allons découvrir les principes dans cet article, alors que les mises en œuvre seront abordées dans l’article suivant.
Au cœur du Domain Driven Design (DDD) se trouve la reconnaissance que le développement de logiciels n'est pas une activité isolée, mais plutôt un moyen de répondre aux besoins d'un métier particulier. La conception axée sur le domaine fournit un ensemble de principes et de pratiques qui peuvent aider les développeurs à créer des logiciels qui s'alignent sur les buts et les objectifs du métier et offrent finalement de la valeur ajoutée aux utilisateurs finaux.
1. Création d’un langage ubiquitaire
Le Domain Driven Design (DDD) met en lumière l'utilisation d'un langage partagé entre les développeurs et les experts du domaine. Cela permet de s'assurer que tout le monde soit sur la même longueur d'onde et de partager une compréhension commune du domaine.
Pour créer un langage ubiquitaire, il est important d'établir une terminologie claire et cohérente partagée entre les développeurs et les experts du domaine.
Supposons que nous ayons un domaine où nous vendons des produits et que nous souhaitions créer un site Web de commerce électronique. Nous devons nous assurer que toutes les personnes impliquées dans le projet, y compris les développeurs et les experts du domaine, utilisent un langage commun pour décrire les entités et les concepts impliqués dans le domaine.
Nous pouvons prendre un exemple simple de panier d'achat dans le domaine du commerce électronique. Si le développeur n'a pas assez d'échanges avec l'expert du domaine, il le coderait comme ci-dessous.
Dans le code ci-dessus, il n'y a pas de langage partagé ni de compréhension du domaine. Les méthodes sont nommées de manière générique et ne représentent pas le domaine du commerce électronique.
Nous pouvons également utiliser le langage partagé pour définir des méthodes dans nos classes. Par exemple, ajoutons une méthode à la classe Product qui calcule le prix réduit en fonction d'un pourcentage :
Dans le code ci-dessus, les noms de classe et de méthode sont plus représentatifs du domaine du commerce électronique. Le langage du domaine est utilisé pour nommer la classe et les méthodes, et la méthode de calcul du prix total est nommée de manière plus appropriée. Cela permet aux experts du domaine et aux développeurs de communiquer et de se comprendre plus facilement.
En utilisant un langage ubiquitaire pour définir nos classes, méthodes et variables de modèle dans le domaine, nous nous assurons que toutes les personnes impliquées dans le projet ont une compréhension commune du domaine et peuvent communiquer efficacement.
2. Concentration sur le domaine métier
Le Domain Driven Design (DDD) insiste sur l'importance de comprendre le domaine métier et de créer un modèle de domaine qui le représente avec précision.
Pour mettre en œuvre ce principe, il est important de travailler en étroite collaboration avec des experts du domaine pour identifier et comprendre le domaine métier. Une fois le domaine compris, un modèle de domaine peut être créé pour mieux s'aligner aux exigences et aux objectifs du métier.
Par exemple, ShoppingCart est un élément important du domaine métier dans une application de commerce électronique. Il représente un conteneur contenant des articles qu'un client a l'intention d'acheter.
Il est important de comprendre le domaine métier et de créer un modèle de domaine qui le représente avec précision pour concevoir notre module.
Dans le cas du ShoppingCart, nous pouvons voir qu'il y a certains attributs, tels que les articles qui sont actuellement dans le panier et le total_price de ces articles. Dans un premier temps, il présente aussi les principales fonctionnalités telles que l'ajout d'articles au panier, la suppression d'articles du panier et le calcul du prix total des articles dans le panier.
En modélisant le ShoppingCart de cette manière, nous pouvons créer un système logiciel qui s'aligne plus étroitement sur les besoins du métier.
3. Modélisation du domaine
Le Domain Driven Design (DDD) encourage la création d'un modèle de domaine qui capture les processus métier, les règles et les concepts de manière structurée.
Nous pouvons revoir notre modélisation de ShoppingCart développée dans l'exemple précédent. La classe ShoppingCart possède à la fois les méthodes calculate_total_price et calculate_shipping_cost, ce qui signifie qu'elle est étroitement liée à la fois à la logique de tarification et d'expédition. Si les exigences commerciales en matière de tarification ou d'expédition devaient changer, la classe ShoppingCart devrait être modifiée, ce qui pourrait entraîner des effets secondaires inattendus et un risque accru d'introduction de bogues. Nous pouvons structurer nos modèles de domaine d'une nouvelle manière.
Dans ce cas, la classe ShoppingCart n'a que la méthode calculate_total_price, ce qui signifie qu'elle est découplée de la logique d'expédition. La politique d'expédition est maintenant représentée par la classe ShippingPolicy, qui a la responsabilité unique de calculer les frais d'expédition en fonction d'une liste d'articles. Si les exigences commerciales en matière d'expédition devaient changer, seule la classe ShippingPolicy devrait être modifiée, ce qui réduit le risque d'introduction de bogues et rend le code plus maintenable.
4. Appui sur les racines d’agrégat
Le Domain Driven Design (DDD) recommande de s’appuyer sur les racines d’agrégat [1], en tant qu'éléments principaux dans le modèle de domaine, car elles représentent les bornes ou frontières de cohérence transactionnelle du système.
Dans le commerce électronique, nous avons les classes Product, ShoppingCart et Order pour passer une commande comme indiqué dans le code suivant.
Dans cet exemple, la classe Order contient toutes les informations sur une commande, y compris le nom du client, les articles commandés et le prix total. Cependant, aucun objet unique ne représente la frontière de cohérence transactionnelle du système.
Maintenant, voici un exemple de la même application de commerce électronique utilisant des racines d’agrégat.
Dans cet exemple, la classe Order est la racine d’agrégat, représentant la frontière de cohérence transactionnelle du système. Les classes ShoppingCart et Product sont toujours présentes, mais elles n'ont aucune connaissance directe de la classe Order. Du fait, OrderService gère le placement de commandes et l'ajout d'articles aux commandes. En utilisant des racines d’agrégat, le code est organisé d'une manière qui reflète mieux le modèle de domaine et permet un code plus maintenable et extensible.
5. Utilisation des contextes bornés
Le Domain Driven Design (DDD) recommande de diviser un grand système en contextes bornés plus petits et plus gérables, chacun avec son propre modèle de domaine.
Pour utiliser des contextes bornés, nous divisons un grand système en contextes bornés plus petits et plus gérables, chacun avec son propre modèle de domaine.
Dans le domaine du commerce électronique, nous pouvons avoir des domaines Product, ShoppingCart et Order. Sans la notion de contexte borné, ils sont placés dans le même fichier ou module, conduisant à une architecture monolithique comme illustré ci-dessous, avec confusion et conflits dans la compréhension du domaine. Toute modification ou mise à jour d'un domaine peut affecter d'autres domaines, ce qui rend difficile la maintenance et la mise à l'échelle du système. Le code serait moins modulaire, ce qui rendrait plus difficile le test et le débogage de fonctionnalités spécifiques.
Maintenant, les contextes bornés peuvent le rendre différent.
Dans cette amélioration, nous avons trois contextes bornés possédant son propre modèle de domaine et son propre ensemble de responsabilités :
Product Management, reponsable de la gestion des produits
Shopping, reponsable de la gestion des paniers
Order Management, responsable de la gestion des commandes
En divisant le système en ces contextes plus petits et plus gérables, nous pouvons améliorer l'organisation globale et la maintenabilité de la base de code.
6. Bénéfices de la carte de contexte
La conception pilotée par le domaine (DDD) recommande d'utiliser la carte de contexte pour gérer les relations entre les contextes bornés et s'assurer qu'ils marchent ensemble de manière efficace.
Comme nous l'avons déjà vu Separate Ways [1] dans la carte de contexte, dans le domaine du commerce électronique, nous pouvons avoir les contextes bornés ProductManagement et OrderManagement comme ci-dessous.
Dans le code ci-dessus, les classes Product et Order sont étroitement couplées, ce qui rend difficile la modification ou l'ajout de fonctionnalités à l'une ou l'autre des classes, sans affecter l'autre. En d'autres termes, il n'y a pas de séparation claire des préoccupations. Cela peut compliquer la gestion des relations entre ces modules. Il faut s'assurer qu'ils fonctionnent ensemble de manière efficace.
Dans le code ci-dessus, le contexte ProductCatalog est responsable de la gestion des produits, et le contexte OrderManagement s'appuie sur lui pour calculer le prix total d'une commande. Cela rend le système plus modulaire et plus facile à entretenir, car les modifications apportées à un contexte sont moins susceptibles d'avoir un effet de régression sur d'autres contextes.
En séparant les préoccupations et en utilisant la carte de contexte, il devient plus facile de gérer la complexité et de s'assurer que le système est maintenable dans le temps.
7. Les événements de domaine
La conception pilotée par le domaine (DDD) invite l'utilisation d'événements de domaine à capturer les changements significatifs dans le domaine et pour permettre la communication entre des contextes bornés.
Nous pouvons maintenant poursuivre notre développement du projet de commerce électronique avec des contextes bornés d'inventaire et de paiement. Dans le premier exemple sans événements de domaine, la classe Order sera étroitement couplée aux classes Inventory et Payment, car elle a une connaissance directe de leurs méthodes. De plus, le processus de passation de commande est une fonction longue et étroitement couplée qui gère de multiples responsabilités.
Dans cette version du code, nous avons ajouté une classe OrderPlacedEvent dans le contexte borné OrderManagement, tout en capturant le changement significatif de la passation d'une commande. Au lieu d'appeler directement les classes Inventory et Payment depuis la classe Order, nous publions maintenant l'objet OrderPlacedEvent, qui est chargé de notifier aux autres contextes bornés qu'une commande a été passée.
Cette implémentation présente plusieurs avantages par rapport à la version sans événements de domaine :