Conception basée sur le domaine: une recette pour un pragmatique


Pourquoi les DDD sont-ils généralement abordés du mauvais côté? Et de quel côté voulez-vous? Qu'est-ce que les girafes et les ornithorynques ont à voir avec tout ça?

Surtout pour Habr - une transcription textuelle du rapport "Conception axée sur le domaine: une recette pour un pragmatique". Le rapport a été fait lors de la conférence DotNext .NET, mais il peut être utile non seulement aux donateurs, mais à tous ceux qui sont intéressés par DDD (nous pensons que vous maîtriserez quelques exemples de code C #). Un enregistrement vidéo du rapport est également joint.



Bonjour à tous, je m'appelle Alexey Merson. Je vais vous dire ce qu'est la conception pilotée par domaine et quelle est son essence, mais d'abord, essayons de comprendre pourquoi elle est nécessaire.



Martin Fowler a déclaré: "Il y a peu de choses qui sont moins logiques que la logique métier." La girafe est certainement l'une de ces rares. La distance entre le cerveau et le larynx d'une girafe n'est que de quelques centimètres. Cependant, le nerf qui les relie atteint 4 mètres. D'abord, il descend par tout le cou, il fait le tour de l'artère puis il revient presque de la même façon.

À première vue, il n'y a vraiment aucune logique. Mais ce n'est qu'un héritage dense laissé par d'anciens poissons. Chez les poissons, comme vous le savez, il n'y a pas de cou, donc ce nerf parcourt le chemin optimal. Et lorsque les mammifères sont apparus après plusieurs millions d'années de refactoring, le nerf a dû être étendu pour maintenir la compatibilité descendante. Eh bien, ne pas remodeler à cause de certaines girafes?

Mais la girafe va bien, car il y a un ornithorynque.


Pensez-y. Le mammifère Avec un bec. Vit principalement dans l'eau. Pond des œufs. Et en plus, toxique. Il peut sembler que la seule explication logique de son existence est qu'il vient d'Australie.

Mais je pense que tout est plus banal. L'entrepreneur a simplement oublié la conception et économisé avec StackOverflow, enfin, ou ce qu'il y avait à l'époque.

Je sais ce que vous pensez maintenant: "Alexey, eh bien, vous nous avez promis une conception pilotée par domaine, et voici une sorte de" Dans le monde animal "!"

Chers collègues, qu'est-ce que le développement? Le développement, c'est lorsque nous prenons une partie du monde réel, un processus métier, et que nous le transformons en code, c'est-à-dire en construisant un modèle logiciel. Quels problèmes nous attendent en cours de route?

Le premier est la complexité des processus métier eux-mêmes, c'est-à-dire la difficulté à comprendre comment fonctionne l'entreprise, quels processus se déroulent là-bas, par quelle logique ils sont construits.

Le deuxième problème est la mise en œuvre de ces processus métier sous forme de code, l'utilisation des bons modèles, les bonnes approches, etc. C'est aussi un sujet assez compliqué.

Regardez, les processus commerciaux sont comme cette girafe: ils ont commencé avec les plus simples unicellulaires, puis «ça» vous regarde, et personne ne comprend «d'où ça vient» ou «comment ça marche».

Pour construire un modèle réussi d'un tel processus, vous devez d'abord répondre à la question «pourquoi?». Pourquoi voulons-nous construire ce modèle? Quels objectifs voulons-nous atteindre? Après tout, si le client voulait une girafe en peluche, mais a eu le courage, alors il sera contrarié, même si la digestion dans ce modèle est mise en œuvre pour un régal pour les yeux. Et le client perdra non seulement de l'argent et du temps, il perdra confiance en nous en tant que développeurs, et nous perdrons notre réputation et notre client.

Mais même si nous avons déterminé les objectifs, cela ne garantit toujours pas que nous n'obtiendrons pas l'ornithorynque en conséquence. Le fait est que l'objectif est peu compréhensible. L'objectif doit être atteint. Et cela nous aide à concevoir un domaine.

L'objectif principal de la conception pilotée par domaine est de lutter contre la complexité des processus métier, leur automatisation et leur implémentation dans le code. «Domaine» se traduit par «domaine», et le développement et la conception dans le cadre de cette approche sont éloignés du domaine.

La conception pilotée par domaine comprend de nombreux éléments. Cette conception stratégique, l'interaction entre les gens, les approches de l'architecture et les modèles tactiques - c'est tout un arsenal qui fonctionne vraiment et aide vraiment à faire des projets. Il n'y a qu'un «mais». Avant de commencer à gérer la complexité de la conception pilotée par domaine, vous devez apprendre à gérer la complexité de la conception pilotée par domaine elle-même.



Quand une personne commence à plonger dans ce sujet, une énorme quantité d'informations lui tombe dessus: des livres épais, un tas d'articles, des modèles, des exemples. Tout cela est déroutant, et il est facile, comme on dit, de ne pas remarquer derrière les arbres de la forêt. Je l'ai ressenti une fois sur moi-même, mais aujourd'hui, je veux partager mon expérience avec vous et vous aider à traverser cette jungle, en commençant enfin à utiliser la conception pilotée par domaine.

Le terme Domain-Driven Design lui-même a été proposé par Eric Evans en 2003 dans son livre imprononçable, que la communauté appelle simplement le Blue Book. Le problème est que la première moitié du livre Evans parle de schémas tactiques (vous les connaissez tous: il s'agit d'usines, d'entités, de référentiels, de services), et les gens n'obtiennent généralement pas la seconde moitié. L’homme regarde: tout est familier, je vais chercher l’application DDD.

À droite, ce qui se passe si vous lancez follement des modèles tactiques sur le compilateur. Gauche - si vous utilisez des modèles stratégiques.



Depuis la sortie du Blue Book, une communauté DDD assez forte s'est formée, beaucoup de choses ont été repensées. Oui, et Evans lui-même a admis qu'il ne comprenait plus comment il pouvait mettre fin à une chose aussi importante que la conception stratégique.

Et 10 ans plus tard, en 2013, le Livre rouge a été publié par Vaughn Vernon. Et dans ce livre, la présentation est déjà construite dans le bon ordre: elle commence par la conception stratégique, par les bases. Et lorsque le lecteur a reçu la base nécessaire, il commence déjà à parler des modèles tactiques et des détails de mise en œuvre.

Habituellement, dans les rapports sur DDD, ils recommandent de lire Evans, sur Internet, il y a même des manuels entiers dans quel ordre vous devez lire les chapitres pour une immersion appropriée. Je recommande de le faire plus facilement: commencez par le Livre rouge, lisez-le, puis passez au bleu.

Et puisque la conception stratégique est une chose si importante, parlons de ses idées clés.

"Idées clés de la conception stratégique"


Dans tout projet d'automatisation d'entreprise, il y a toujours des experts du domaine. Ce sont des personnes qui comprennent le mieux le fonctionnement des processus métier à modéliser. Ceux-ci peuvent être des développeurs, des dirigeants et des cadres supérieurs de premier plan. En général, cela peut être n'importe qui, si seulement il comprend les processus commerciaux avec lesquels nous devons traiter.



D'autre part, il y a des experts techniques: développeurs, architectes directement impliqués dans l'automatisation et la mise en œuvre des applications. Dans l'exemple illustré, le client voulait probablement un chemin de fer pour enfants, mais il s'est avéré être une sorte de monstre.

Pourquoi cela se produit-il? Parce que l'interaction entre les experts techniques et les experts du domaine dans une situation typique ressemble à ceci: il y a un grand-grand mur entre eux, et un gestionnaire marche le long du haut de ce mur et essaie d'abord d'entendre ce qu'ils crient d'un côté du mur, puis il essaie de le crier au mieux des bundles de l'autre côté du mur, et ainsi de suite en cercle.

Parfois, un gestionnaire est sourd, alors toute une chaîne de tels gestionnaires peut être constituée, ce qui, bien sûr, ne contribue pas à la réussite du projet. Et comment ça devrait être?



Il doit y avoir une interaction constante. Experts techniques, experts du domaine - tous les participants au projet doivent constamment maintenir la communication, se synchroniser, discuter des objectifs, des moyens de les atteindre et pourquoi faisons-nous tout cela.

Et nous arrivons ici au premier et, probablement, au point clé le plus important de la conception stratégique et de la conception pilotée par le domaine en général.



La communication entre les participants au projet forme ce que la conception pilotée par domaine appelle un langage omniprésent. Il n'est pas un dans le sens où il l'est pour toutes les occasions. Bien au contraire. Il est unique dans le sens où tous les participants y communiquent, toute discussion se déroule en termes d'une seule langue, et tous les artefacts doivent être au maximum en termes d'une seule langue, c'est-à-dire à partir de TK et se terminant par un code.

Scénarios d'affaires


Pour une discussion plus approfondie, nous avons besoin d'une sorte de scénario d'entreprise. Imaginons cette situation:



Le directeur du groupe JUG.ru vient à nous et dit: "Les gars, le flux des rapports augmente, les gens, en général, sont torturés pour tout faire manuellement ... Automatisons le processus de préparation de la conférence." Nous répondons: "D'accord!" - et se mettre au travail.

Le premier scénario que nous automatiserons est le suivant: «L'orateur soumet une demande de rapport à un événement spécifique et ajoute des informations sur son rapport.» Que voyons-nous dans ce scénario? Qu'est-ce qu'un orateur, il y a un événement, et il y a un rapport, ce qui signifie qu'il est déjà possible de construire le premier modèle de domaine.



Nous avons ici un modèle de domaine: Conférencier - conférencier, Talk - report, Event - event. Mais le modèle de domaine ne peut pas être illimité, ne peut pas tout couvrir, sinon il deviendra flou et perdra le focus, donc le modèle de domaine doit être limité par quelque chose. Ceci est le prochain point clé.



Le modèle de domaine et le langage omniprésent sont tous deux limités par le contexte que la conception pilotée par domaine appelle contexte borné. Il restreint le modèle de domaine de telle manière que tous les concepts qu'il contient sont sans ambiguïté et que tout le monde comprend ce qui est en jeu.

S'ils disent «utilisateur», alors tout doit être clair à la fois, il doit avoir un rôle compréhensible, un sens compréhensible, il ne doit pas être une sorte d'utilisateur abstrait du point de vue de l'industrie informatique.



Dans notre cas, ce modèle de domaine est valable pour le contexte de la préparation de la conférence, c'est donc dans le contexte que nous appellerons le «contexte de planification d'événement». Mais pour que l'orateur ajoute quelque chose, change des informations, il doit d'une manière ou d'une autre se connecter, il doit avoir des droits. Et ce sera déjà un autre contexte, «contexte d'identité», dans lequel il y aura une sorte de leurs propres entités: utilisateur, rôle, profil.

Et regardez ce que la chose est ici. Lorsqu'une personne se connecte au système et a l'intention d'entrer une sorte d'information, physiquement, c'est la même personne, mais dans différents contextes, elle est représentée par différentes entités, et ces entités ne sont pas directement liées.

Si nous prenions et, par exemple, héritions Speaker de User, nous mélangerions des choses qui ne peuvent pas être mélangées, et certains attributs pourraient être mélangés par la logique. Et le modèle perdrait le focus sur le sens spécifique qu'il a, étant divisé en plusieurs contextes.

Démo: Service commercial


Écartons un peu de la théorie sèche et regardons le code.

Une conférence n'est pas seulement la préparation d'un contenu, mais aussi des ventes. Imaginons qu'un service de vente de billets ait déjà été écrit, et un responsable des ventes vient nous voir et dit: «Les gars! Une fois que quelqu'un a écrit ce service, découvrons-le, quelque chose n'est pas clair pour moi comment la remise pour les clients réguliers est considérée. "

Après avoir discuté avec le gestionnaire, nous découvrons que tout le scénario de ce service est le suivant: en cliquant sur Commander, le prix final du billet est considéré en tenant compte de la réduction client régulière, et la commande passe à l'état «En attente de paiement».

Le code que nous allons maintenant analyser peut être consulté séparément dans le référentiel .

Solution ouverte, regardez la structure:



Il semble que tout semble bien: il y a Application et Core (apparemment, les gens connaissent les couches), Repository ... Apparemment, la personne a maîtrisé la première moitié d'Evans.

Ouvrez OrderCheckoutService. Que voyons-nous là-bas? Voici le code :

public void Checkout(long id) { var ord = _ordersRepository.GetOrder(id); var orders = _ordersRepository.GetOrders() .Count(o => o.CustomerId == ord.CustomerId && o.StateId == 3 && o.OrderDate >= DateTime.UtcNow.AddYears(-3)); ord.Price *= (100 - (orders >= 5 ? 30m : orders >= 3 ? 20m : orders >= 1 ? 10m : 0)) / 100; ord.StateId = 1; _ordersRepository.SaveOrder(ord); } 


Nous regardons la ligne avec le prix: ici le prix change. Nous appelons notre directeur des ventes et lui disons: "Ici, en bref, la remise est considérée ici, tout est clair":

 ord.Price *= (100 - (orders >= 5 ? 30m : orders >= 3 ? 20m : orders >= 1 ? 10m : 0)) / 100; 


Il regarde par-dessus son épaule: «Oh! Voilà à quoi ressemble Brainfuck! Et ils m'ont en quelque sorte dit que les gars écrivaient en C # ».

De toute évidence, le développeur de ce code a bien répondu à une interview sur les algorithmes et les structures de données. J'ai écrit à l'école des olympiades dans le même style. Après un certain temps, en utilisant le formatage et le refactoring obscène , nous découvrons ce qui est quoi et expliquons à notre directeur des ventes qui souffre depuis longtemps que la logique est la suivante: si le nombre de commandes au cours des 3 dernières années n'est pas inférieur à un, alors il reçoit une remise de 10% , pas moins de trois à 20% et pas moins de cinq à 30%. Il part joyeux - maintenant, il est clair comment tout cela fonctionne.

Je pense que beaucoup ont lu le Clean Code de Bob Martin. Là, il dit à propos de la règle du boy-scout: "Le parking après notre départ devrait être plus propre qu'il ne l'était avant notre arrivée." Par conséquent, refactorisons ce code afin qu'il ait l'air humain et corresponde à ce dont nous avons parlé un peu plus tôt à propos du langage omniprésent et de son utilisation dans le code.

Voici le code refactorisé.

  public class DiscountCalculator { private readonly IOrdersRepository _ordersRepository; public DiscountCalculator(IOrdersRepository ordersRepository) { _ordersRepository = ordersRepository; } public decimal CalculateDiscountBy(long customerId) { var completedOrdersCount = _ordersRepository.GetLast3YearsCompletedOrdersCountFor(customerId); return DiscountBy(completedOrdersCount); } private decimal DiscountBy(int completedOrdersCount) { if (completedOrdersCount >= 5) return 30; if (completedOrdersCount >= 3) return 20; if (completedOrdersCount >= 1) return 10; return 0; } } 


La première chose que nous faisons est de transférer le calcul de la remise vers un DiscountCalculator distinct, dans lequel la méthode CalculateDiscountBy customerId apparaît. Tout est lu humainement, tout est clair: quoi, pourquoi et comment. À l'intérieur de cette méthode, nous voyons que nous avons globalement deux étapes pour calculer la remise. Premièrement: nous recevons quelque chose du référentiel de commandes, tout est selon le cas d'utilisateur, vous n'avez même pas besoin d'aller à l'intérieur si ce n'est pas la partie qui vous intéresse maintenant. Le fait est que nous obtenons le nombre de certaines commandes terminées, après quoi nous considérons immédiatement la deuxième remise pour cette quantité comme la deuxième étape.

Si nous voulons voir comment cela est considéré, nous allons à DiscountBy, et ici presque la même chose est écrite en anglais presque humain que notre "type de brainfair" était avant, tout est clair et précis.

La seule question qui pourrait se poser est de savoir dans quelles unités la remise est mesurée. Il serait possible d'ajouter le mot «pour cent» dans le nom de la méthode pour le clarifier, mais d'après le contexte et les chiffres impliqués, devinez probablement que ce sont des pourcentages, et pour plus de concision, il peut être omis. Si nous voulons voir le nombre de commandes, nous irons dans le code du référentiel et verrons. Maintenant, nous ne le ferons pas. Dans notre service, nous devons ajouter une nouvelle dépendance DiscountCalculator. Et voyons ce que nous avons fini avec la deuxième version de la méthode Checkout.

 public void CheckoutV2(long orderId) { var order = _ordersRepository.GetOrder(orderId); var discount = _discountCalculator.CalculateDiscountBy(order.CustomerId); order.ApplyDiscount(discount); order.State = OrderState.AwaitingPayment; _ordersRepository.SaveOrder(order); } 


Regardez, la méthode Checkout reçoit orderId, puis reçoit un orderId par orderId, selon le CustomerId de cette commande, elle considère la remise à l'aide du calculateur de remise, applique la remise à la commande, définit le statut sur AwaitingPayment et enregistre la commande. Nous avions un script en russe sur la diapositive, mais ici nous lisons pratiquement la traduction de ce script en anglais et tout est clair, tout est évident.

Voyez-vous quel est le charme? Ce code peut être montré à n'importe qui: pas seulement aux programmeurs, mais au contrôle qualité, aux analystes et aux clients. Ils comprendront tous ce qui se passe, car tout est écrit en langage humain. J'utilise ceci dans notre projet, vraiment QA peut regarder quelques morceaux, vérifier avec Wiki et comprendre qu'il y a une sorte de bogue. Parce que le Wiki le dit et que le code est un peu différent, mais il comprend ce qui se passe là-bas, bien qu'il ne soit pas développeur. Et de la même manière, nous pouvons discuter du code avec l'analyste et en discuter en détail. Je dis: "Regardez, voici comment cela fonctionne dans le code." Notre dernier recours n'est pas le Wiki, mais le code. Tout fonctionne comme il est écrit dans le code. Il est très important d'utiliser un langage omniprésent lors de l'écriture de code.



Ceci est le troisième point clé.

Il y a tellement de confusion à propos de la conception pilotée par domaine dans des choses comme le domaine, le sous-domaine, le contexte délimité, la façon dont ils se rapportent à ce qu'ils signifient. Il semble que tout le monde limite quelque chose, tous sont en quelque sorte bien rangés. Mais il n'est pas clair alors quelle est la différence, pourquoi ils sont si différents inventés.



Le domaine est une chose globale, c'est un domaine mondial dans lequel cette entreprise particulière fait de l'argent. Par exemple, pour DotNext c'est une conférence, pour Pyaterochka c'est une vente au détail de marchandises.

Les grandes sociétés peuvent avoir plusieurs domaines. Par exemple, Amazon est engagé à la fois dans la vente de biens via Internet et dans la fourniture de services cloud, ce sont des domaines différents.

Néanmoins, c'est quelque chose de mondial et ne peut pas être automatisé directement, même il est difficile d'enquêter. Pour l'analyse, le domaine est inévitablement divisé en sous-domaines, c'est-à-dire en sous-domaines.



Les sous-domaines font partie d'une entreprise qui, dans notre langue, sont hautement connectés, c'est-à-dire qu'il s'agit d'une sorte de processus logiques isolés qui interagissent les uns avec les autres à un niveau majeur.

Par exemple, si nous prenons une boutique en ligne, ce sera la formation et le traitement des commandes, ce sera la livraison, c'est le travail avec les fournisseurs, c'est le marketing, c'est la comptabilité. Voici quelques-unes de ces pièces - c'est ce que l'entreprise est divisée.



Du point de vue de DDD, les sous-domaines sont divisés en trois types. Et ici, je veux dire encore une chose: souvent dans les livres et articles, le sous-domaine est simplement réduit au domaine, mais généralement dans le cas où il est combiné avec le type de sous-domaine. Autrement dit, quand ils disent «domaine principal», ils signifient sous-domaine principal, s'il vous plaît ne vous y trompez pas. Cela m'a époustouflé au début.

Les sous-domaines sont divisés en trois types.



Le premier et le plus important est Core. Le noyau est le sous-domaine principal, c'est l'avantage concurrentiel de l'entreprise, ce qui fait de l'argent de cette entreprise, en quoi elle diffère de ses concurrents, son savoir-faire, peu importe comment vous l'appelez. Si nous prenons la conférence DotNext, alors c'est le contenu. Vous êtes tous venus ici pour du contenu, s'il n'y en avait pas ici, vous n'iriez pas ou n'iriez pas à une autre conférence. Il n'y aurait pas de DotNext sous la forme dans laquelle il se trouve.



Le deuxième type est Supporting Subdomain. C'est aussi une chose importante pour gagner de l'argent, c'est aussi quelque chose sans lequel c'est impossible, mais ce n'est pas une sorte de savoir-faire, un réel avantage concurrentiel. C'est ce que prend en charge Core Subdomain. Du point de vue de l'application de la conception pilotée par domaine, cela signifie que moins d'efforts sont consacrés à la prise en charge du sous-domaine, toutes les forces principales sont lancées sur Core.

Un exemple pour le même DotNext est le marketing. C'est impossible sans marketing, sinon personne n'aurait été au courant de la conférence, mais sans marketing de contenu n'est pas nécessaire.



Et enfin, le sous-domaine générique. Le générique est une tâche commerciale typique qui, en règle générale, peut être automatisée avec des produits finis ou externalisée. C'est ce qui est également nécessaire, mais cela ne nécessite pas nécessairement une mise en œuvre indépendante par nous, et plus encore, ce sera généralement une bonne idée d'utiliser un produit tiers.

Par exemple, vendre des billets. DotNext vend des billets via TimePad. Ce sous-domaine est parfaitement automatisé par TimePad, et vous n'avez pas besoin d'écrire un deuxième TimePad vous-même.



Et enfin, contexte borné. Le contexte délimité et le sous-domaine sont toujours quelque part à proximité, mais il existe une différence significative entre eux. C'est très important.



Il y a une question sur StackExchange en quoi le contexte borné diffère du sous-domaine. Le sous-domaine est une entreprise, un morceau du monde réel, c'est le concept d'un espace de déclaration de problème. Le contexte borné limite le modèle de domaine et le langage omniprésent, c'est-à-dire le résultat de la modélisation et, par conséquent, le contexte borné est le concept d'espace de solution. Dans le processus de mise en œuvre du projet, une sorte de mappage des sous-domaines a lieu dans des contextes limités.



Un exemple classique: la comptabilité en tant que sous-domaine, la façon dont le processus est mappé, est automatisée, par exemple, 1C Bookkeeping, Elba ou «My business» - est en quelque sorte automatisée par un produit. C'est le contexte borné de la comptabilité, dans lequel il y a son langage omniprésent, sa propre terminologie. C’est la différence entre eux.



Si nous revenons à DotNext, comme je l'ai dit, les tickets sont mappés à TimePad et le contenu qui est notre sous-domaine principal est mappé à une application personnalisée que nous développons pour la gestion de contenu.

Taille du contexte borné


Il y a un moment qui soulève de nombreuses questions. Comment choisir la bonne taille pour un contexte délimité? Dans les livres, on peut trouver une telle définition: "Le contexte borné doit être exactement tel que le langage omniprésent est complet, cohérent, sans ambiguïté, sans ambiguïté, cohérent." Définition cool, dans le style d'un mathématicien d'une célèbre blague: très précis, mais inutile.

Voyons comment comprendre tout de même: s'il doit s'agir d'une solution, d'un projet ou d'un espace de noms - quelle échelle doit être attachée au contexte délimité?



La première chose que vous pouvez lire presque partout: idéalement, un sous-domaine doit être mappé à un contexte limité , c'est-à-dire être automatisé par un contexte limité. Cela semble logique, car à la fois là et il y a des limites d'un processus métier distinct, dans les deux cas, certains termes commerciaux, une seule langue apparaît. Mais ici, vous devez comprendre que c'est une situation idéale, vous ne l'aurez pas nécessairement, et il n'est pas nécessaire d'essayer d'y parvenir.

Parce que, d'un côté, un sous-domaine peut être assez volumineux, et vous pouvez obtenir plusieurs applications ou services qui vont l'automatiser, il peut donc s'avérer que plusieurs contextes limités correspondent à un sous-domaine.

Mais il y a une situation inverse, en règle générale, c'est typique de Legacy. Autrement dit, lorsqu'ils ont créé une grande, grande application qui automatise tout dans le monde dans cette entreprise, le contraire se révélera. Une application est un contexte borné, là le modèle sera probablement une sorte d'ambiguïté, mais les sous-domaines n'y ont pas disparu, respectivement, un contexte borné correspondra à plusieurs sous-domaines.

Lorsque l'architecture des microservices est devenue à la mode, une autre recommandation est apparue (bien qu'elles ne se contredisent pas): un contexte limité par microservice . Encore une fois, cela semble logique, les gens le font vraiment. Parce que le microservice doit assumer une fonction claire, qui dispose en interne d'une connectivité élevée et communique avec d'autres services via une sorte d'interaction. Si vous utilisez une architecture de microservice, vous pouvez prendre cette recommandation par vous-même.

Mais ce n'est pas tout. Permettez-moi de vous rappeler une fois de plus que la conception pilotée par domaine concerne beaucoup: la langue, les gens. Et vous ne pouvez pas ignorer les gens et ne faire que des critères techniques dans ce domaine. Par conséquent, j'ai écrit ceci: un contexte est égal à X-man . Je pensais que x est environ 10, mais nous avons discuté un peu avec Igor Labutin ( twitter.com/ilabutin ) et la question est restée ouverte.

Ici, il est important de comprendre ceci: une seule langue reste unifiée pendant que tous les participants parlent, discutent et tout le monde la comprend sans ambiguïté. Il est clair qu'un nombre infini de personnes ne peuvent pas parler la même langue. Notre histoire de l'humanité le montre clairement. Dans tous les cas, certains dialectes apparaissent, certaines de leurs significations, maintenant vous pouvez même ajouter des mèmes et ainsi de suite. D'une manière ou d'une autre, la langue deviendra floue.

Par conséquent, il faut comprendre que le nombre de personnes qui utilisent ce langage unique et, par conséquent, participent au développement, à l'automatisation, est limité. Les livres parlent également de certaines raisons politiques: si deux équipes travaillent sous la direction de différents gestionnaires et travaillent sur le même contexte délimité, et pour une raison quelconque, ces gestionnaires ne sont pas amis, les conflits commenceront et la concentration sera perdue. Par conséquent, il sera beaucoup plus simple et plus correct de créer deux contextes bornés pour chaque commande et de ne pas essayer de combiner ce qui n'est pas combiné.

Architecture et gestion des dépendances


Du point de vue de la conception pilotée par domaine, peu importe l'architecture que vous choisissez. La conception pilotée par domaine ne concerne pas cela; la conception pilotée par domaine concerne le langage et la communication.



Mais il y a un point important, du point de vue des critères de choix de l'architecture qui nous intéresse du point de vue de la conception pilotée par le domaine: notre objectif est de débarrasser au maximum la logique métier des dépendances tierces . Parce que, dès que des dépendances tierces apparaissent, la terminologie apparaît, des mots apparaissent qui n'entrent pas dans une seule langue et commencent à joncher notre logique métier.



Regardons un exemple classique d'architecture: l'architecture bien connue à trois couches. Dès qu’ils n’appellent pas une couche de domaine (ici la couche d’entreprise): l’entreprise, le cœur et le domaine sont tous les mêmes. Dans tous les cas, il s'agit de la couche dans laquelle se trouve la logique métier, et si elle dépend de la couche de données, cela signifie que certains concepts de la couche de données se déverseront d'une manière ou d'une autre dans la couche de domaine et la joncheront.



L'architecture à quatre couches est essentiellement la même, la couche de domaine dépend toujours, et comme elle dépend, des dépendances tierces inutiles y parviendront.



Et dans ce sens, il existe une architecture qui permet d'éviter cela - c'est l'oignon-architecture («oignon»). Sa différence est qu'il est constitué de couches concentriques, les dépendances vont de l'extérieur vers le centre. C'est-à-dire que la couche externe peut dépendre de toutes les couches internes, la couche interne ne peut pas dépendre des couches externes.

La couche la plus externe est l'interface utilisateur dans un sens global (c'est-à-dire qu'elle n'est pas nécessairement une interface utilisateur humaine, elle peut être une API REST ou quoi que ce soit). Et l'infrastructure, qui ressemble souvent en général à des E / S, est la même base de données, en fait, une couche de données. Toutes ces choses sont dans la couche externe. Autrement dit, en raison de laquelle l'application reçoit en quelque sorte des données, des commandes, etc., elles sont supprimées et la couche de domaine se débarrasse de la dépendance à ces choses.

Vient ensuite la couche Application - un thème plutôt holistique, mais c'est la couche dans laquelle se trouvent les scripts, les cas d'utilisateurs. Cette couche utilise la couche domaine pour implémenter ses concepts.

Au centre se trouve la couche de domaine. On le voit, il ne dépend plus de rien, il devient une chose en soi. Et c'est pourquoi la couche de domaine est souvent appelée «Core», car c'est le coeur, c'est ce qui est au centre, ce qui ne dépend pas de choses tierces.



L'une des options pour mettre en œuvre une telle architecture oignon est l'architecture hexagonale, ou «ports et adaptateurs». J'ai apporté cette photo pour intimidation, je n'en parlerai pas. À la fin de l'article, il y a un lien vers l'un des millions d'articles sur cette architecture, vous pouvez le lire.

Un peu sur les modèles tactiques: Interface séparée


Comme je l'ai dit, premièrement, la plupart des schémas tactiques sont familiers à tout le monde et, deuxièmement, tout l'intérêt de mon rapport est qu'ils ne sont pas essentiels. Mais j'aime le modèle d'interface séparée séparément, et je veux en parler séparément.

Revenons au code de notre microservice et voyons ce qui s'est passé avec le référentiel.



La couche de domaine avait l' interface de référentiel IOrdersRepository.cs et son implémentation, OrdersRepository.cs.

 using System.Linq; namespace DotNext.Sales.Core { public interface IOrdersRepository { Order GetOrder(long id); void SaveOrder(Order order); IQueryable<Order> GetOrders(); #region V2 int GetLast3YearsCompletedOrdersCountFor(long customerId); #endregion } } 


Ici, nous avons ajouté ici une certaine méthode pour recevoir des commandes pour les trois dernières années GetLast3YearsCompletedOrdersCountFor.

Et ils l'ont implémenté sous une forme (dans ce cas, via Entity Framework, mais cela peut être n'importe quoi):

  public int GetLast3YearsCompletedOrdersCountFor(long customerId) { var threeYearsAgo = DateTime.UtcNow.AddYears(-3); return _dbContext.Orders .Count(o => o.CustomerId == customerId && o.State == OrderState.Completed && o.OrderDate >= threeYearsAgo); } 


Voyez quel est le problème. Le référentiel s'est retrouvé dans la couche domaine, son implémentation dans la couche domaine, mais le code, commençant par DateTime.UtcNow.AddYears (-3), n'appartient pas intrinsèquement à la couche domaine et n'est pas une logique métier. Oui, LINQ le rend plus ou moins humanisé, mais si, par exemple, il y avait des requêtes SQL ici, tout serait complètement triste.

La signification du modèle d'interface séparée est que l'interface de service que nous utilisons dans la logique de domaine est déclarée dans la couche de domaine. Nous parlons de référentiels et de services similaires dans lesquels les détails de la mise en œuvre de ces services ne sont pas une logique métier. La logique métier est le fait de l'existence de ces services et le fait de leur appel et de leur utilisation dans la couche domaine. Par conséquent, l'interface du référentiel reste dans la couche domaine et l'implémentation passe à la couche infrastructure.

J'ai préparé une autre option. L'interface du référentiel reste dans l'assembly Core, mais l'implémentation passe à Infrastructure.EF.



Ainsi, nous avons apporté à l'infrastructure les concepts qui n'étaient pas propres à la couche domaine. Comme effet secondaire, nous pouvons remplacer cette infrastructure par une autre implémentation. Mais ce n'est pas l'objectif principal, l'objectif principal est, comme je l'ai dit, de débarrasser la logique de domaine des dépendances tierces.

Encore une fois sur la langue


Parlons encore et encore et encore de la langue.

Au tout début, nous avons construit le modèle de domaine «haut-parleur - conversation - événement». Je pense que personne n'a soulevé de questions particulières.

Et voici le scénario sur la base duquel nous avons construit ce modèle de domaine:



Vous voyez, le script est en russe et le modèle de domaine est en anglais.

Pour les développeurs non anglophones, c'est quelque chose avec lequel vous devez vivre constamment.



Chacun de vous, très probablement, fait constamment ce processus: traduit du russe en anglais et vice versa. Ceux qui travaillent avec des clients et des projets anglophones sont un peu plus faciles, car les exigences sont en anglais, les discussions avec les clients en anglais, en règle générale, tous les scénarios sont en anglais, le code est en anglais, et il n'y a que la communication au sein de l'équipe en russe, qui se développe rapidement en anglais (client - client, commande - commande). Et cette charge cognitive, cette surcharge, qui est créée par une traduction constante, recule un peu.

, , , . , .

1, . , — , , .



1. PascalCase , , , , , , , - - .

, - ?



, use case, , . , . C#, , , . , , , .

, , Domain-Driven Design. , , , , C# . .

, - Continuous Integration. , , , - - . , - , , . , 95% , , Continuous Integration, , TeamCity . .

, . . , 1-, , . «» , . , , .



, Domain-Driven Design.

— , . Domain-Driven Design — , . — , , ubiquitous language. , , . , , , .

. , . - , , , , , , , , , , — . .

. . . . , , . DSL-. .NET-, - , , , , . , .

, - . , , ubiquitous language -. .



, , GitHub .

DotNext:
, « , ». DotNext ( 15-16 ) . , 1 , .

Source: https://habr.com/ru/post/fr440772/


All Articles