
Présentation
Dans le premier article, nous avons souligné la portée des pratiques indiquées, pour quels projets elles peuvent être appliquées et pour lesquelles elles ne devraient pas l'être.
Dans cet article, je voudrais donner un bref aperçu des principes de base de DDD, ainsi que partager mon expérience personnelle avec leur application. Nous parlerons plus en détail de la communication et des approches structurelles avec des exemples de leur mise en œuvre.
Dans l'article suivant, j'écrirai les combinaisons possibles des modèles de conception appliqués en tenant compte de leur implémentation, et finalement je donnerai un exemple d'implémentation spécifique d'un petit microservice.
DDD
Rappelez-vous la définition précédente:
La conception pilotée par domaine (DDD) est une approche du développement logiciel pour la satisfaction globale des besoins, en liant étroitement la mise en œuvre aux principaux modèles commerciaux en constante évolution.
Le livre de référence qui décrit la pratique de la construction de systèmes complexes est Domain Driven Dedign (Big Blue Book) d'Eric Evans. Si vous lisez un article de synthèse sur ce sujet, vous le savez déjà. Au moment où vous utilisez DDD dans la pratique, vous devrez le lire. Ce n'est pas le livre le plus facile à lire:
La source canonique de DDD est le livre d'Eric Evans. Ce n'est pas le plus facile à lire dans la littérature sur les logiciels, mais c'est l'un de ces livres qui rembourse amplement un investissement substantiel.
Martin Fowler: 15 janvier 2014
Si vous faites défiler le contenu du livre, il vous semblera pas tout à fait structuré. Mais la carte nous aidera.

Sur la carte figurent les pratiques que nous examinerons aujourd'hui.
La portée des pratiques couvertes dans le livre est énorme. La portée des pratiques qui peuvent être appliquées en dehors de ce livre est encore plus grande. Avant de mettre en service au moins une partie d'entre eux, identifiez vos objectifs. Je vais donner mon propre exemple.
- Augmentez la productivité.
- Écrivez du code compréhensible.
- Mise à l'échelle au niveau du développement logiciel.
Langue unique
Le développement de logiciels conduit rarement à la création de quelque chose de nouveau, en règle générale, il s'agit d'une simulation de quelque chose d'existant.
Un modèle est une représentation d'un objet réel, qui comprend uniquement les propriétés et fonctions nécessaires.
Nous ne pouvons pas créer un produit logiciel qui couvre tout le domaine. Il est possible de n'en reproduire que la partie qui reproduira les fonctionnalités nécessaires.
Un bon exemple de modèle serait une carte topographique. C'est un modèle de terrain. La carte ne contient pas de prairies de champs et de rivières, elle ne reflète que l'emplacement des objets réels les uns par rapport aux autres.
Pour construire un modèle clair et clair pour tout le monde, vous devez parler la même langue. Non seulement Eric Evans nous le dit, mais aussi le bon sens. Si les programmeurs utilisent leurs termes et commercialisent leur propre «argot», les premiers ne comprendront tout simplement pas ce qui doit être fait. Dans ce cas, les entreprises ne pourront pas réaliser le coût réel du développement de l'une ou l'autre «fonctionnalité». Combien de fois avez-vous entendu: "Oui, c'est juste un bouton à ajouter"?
Votre objectif en tant que concepteur de système devrait être d'obtenir la meilleure compréhension mutuelle de toute l'équipe. Comment y parvenir? Commencez à parler. Si les gens commencent à communiquer dans un groupe proche, ils ont un certain ensemble de termes généralement acceptés. Dans différentes entreprises, le processus d'introduction d'un langage commun est susceptible d'être différent. Il peut s'agir soit d'une décision volontaire ou d'une procédure démocratique. La langue peut être indiquée explicitement, et elle n'est pas entrée explicitement, auquel cas ils commencent simplement à la parler. Une bonne technique pour introduire un langage commun est la documentation générale.
Comment conserver la documentation du projet
- Toute communication entre l'entreprise et le développement devrait améliorer votre modèle.
- Après la réunion, enregistrez le résultat sous forme de documentation (artefact Scrum) et montrez cette documentation à tous les participants au processus de développement.
- Utilisez une seule langue dans la documentation.
- Le plus important: ne perdez pas de temps sur la documentation. Vous devez encore écrire du code, et la documentation sera réécrite plusieurs fois, dépenser des ressources coûte cher. Au lieu de jouer longtemps avec l'application de cartographie UML, utilisez une serviette, un stylo et un appareil photo sur votre téléphone.
- La documentation requiert de la discipline, vous ne pouvez pas l'écrire de temps en temps.
- Séparez la documentation:
- Commentaires dans le code - décrivez des moments incompréhensibles directement dans le code, laissez
#ODO:
(supprimez lors de la fusion du code dans master). Exprimez votre opinion dans les commentaires, par exemple, vous devez utiliser l'une ou l'autre béquille lorsque vous travaillez avec du code legasy. - Les commentaires sur le projet
README.md
dans le répertoire racine de votre projet doivent contenir des informations techniques: comment démarrer le projet, comment exécuter des tests, etc. C'est également une bonne idée d'obtenir une carte où vous avez tous les projets et sur quels serveurs ils s'exécutent. Enregistrez séparément tous les accords acceptés. - Et surtout, la base de connaissances. Une collection de documents décrivant les processus commerciaux, c'est la partie des documents qui est disponible pour vous et pour l'entreprise.
- La principale erreur de ceux qui écrivent de la documentation est la redondance. N'essayez pas de tout couvrir et de tout, ne transmettez que le sens général. La documentation doit compléter votre projet, mais ne doit en aucun cas le remplacer. N'écrivez pas tous les termes qui sont seulement ambigus. Si une définition prend plus de deux phrases, c'est une mauvaise définition.
Exemple de documentation:
# . # : : - - email - ## : ### , email , 1 ( email ). ### . email . ### email email . , email. . ### , , . . ### email, , . 2 . ### . , , .
Notez que dans cet exemple nous n'avons pas spécifié de dictionnaire explicite, néanmoins nous avons fixé le concept d' Utilisateur , Autorisation , Enregistrement . La rédaction de cette documentation ne prendra pas plus de 20 minutes à l'expert.
Pour une personne qui n'est pas experte dans le domaine, le processus de rédaction de la documentation est perçu comme quelque chose de compliqué. Il est nécessaire de séparer la collecte des connaissances et l'enregistrement des connaissances collectées. != + .
"Ce que vous appelez l'univers", a déclaré le quatrième, "est, en fait, une accumulation de mondes qui, comme la peau d'un arc, sont les uns au-dessus des autres et sont progressivement séparés les uns des autres."
- Exceptionnellement clairement indiqué! - admiré les abderites. - Étonnamment clair! "Ils pensaient comprendre le philosophe, car ils savaient très bien ce qu'était l'oignon."
Histoire d'Aberdeen, Cristov Martin Wieland
Contextes et domaines limités
Imaginez que nous agissions en tant que concepteur d'une startup progressive. Nous aimons tous la pizza refroidie, maudissant les coursiers et remplissant des formulaires sur le site pendant des heures. Par conséquent, nous avons créé une merveilleuse startup «Quatre tortues et un rat»:
- Il y a un site sur lequel les pizzerias sont enregistrées et affichent leurs plats réels.
- Ces pizzerias n'ont pas leur propre service de messagerie.
- Il y a des clients qui ne peuvent pas atteindre la pizzeria, mais ils sont prêts à passer une commande via le site Web ou l'application mobile.
- «Fonction» tueur: les courriers ne sont pas du personnel spécialement embauché, mais des gens ordinaires qui se sont inscrits via l'application mobile
- Les courriers reçoivent des ordres, après leur exécution ils reçoivent le paiement du travail effectué.
- Il faut plus de temps pour attendre de tels courriers, mais la livraison, et par conséquent la pizza, est moins chère.
Rappelons la documentation que nous avons décrite dans le chapitre précédent. Là, dans notre dictionnaire unique, le terme enregistrement a été utilisé . Mais dans ce projet, nous en avons plusieurs:
- Inscription client
- Inscription à la pizzeria
- Inscription par messagerie
- Enregistrement de la commande
Une langue unifiée appartient à un contexte limité. Le domaine de la documentation ci-dessus est «Système d'autorisation». Essayons d'allouer des domaines pour notre startup.
Mais avant de commencer, regardons un peu la terminologie sur ce qu'est un domaine et ce qu'est un contexte limité.
Domaine (Domain) - une représentation d'une structure d'entreprise réelle qui résout un problème spécifique.
Par exemple: système logistique, système de paiement, système d'autorisation, système de gestion des commandes.
Le domaine est divisé en sous-domaines, qui décrivent des structures plus petites, par exemple: un panier de commandes, un système de construction de routes.
Chaque domaine a un domaine de responsabilité limité - des fonctionnalités limitées.
Contexte délimité - un ensemble de restrictions de domaine qui aide le domaine à se concentrer sur une seule tâche pour sa meilleure solution.
J'aime présenter ce terme comme une telle abstraction. Un domaine est un cercle. Un contexte restreint est un cercle.

Même dans la terminologie DDD, le noyau est alloué.
Le cœur (domaine principal) - le domaine le plus important qui caractérise le plus votre entreprise.
Ainsi, les domaines du projet "Quatre tortues et un rat":
Travailler avec une pizzeria (Pizzarias)
Contexte : b2b tout ce qui concerne les pizzerias
Sous - domaines :
- enregistrement de nouvelles pizzerias
- ajouter un assortiment
- mise à jour de l'état de la disponibilité d'un produit
Travailler avec le client (Clients)
Contexte : b2c, tout ce qui concerne le travail avec les pizzerias
Sous - domaines :
- voir l'assortiment
- matériel d'information
Travailler avec des coursiers (système de livraison)
Contexte : b2e, tout ce qui concerne le travail avec les coursiers
Sous - domaines :
- inscription par messagerie
- affectation des tâches
- enregistrement des demandes de retrait des fonds gagnés par le courrier.
Système de commande
Contexte : noyau. Vous permet de coordonner tous les domaines individuels, offrant un cycle complet de la réception d'une commande à la livraison de pizza à l'utilisateur. Ce n'est pas un artiste, mais joue le rôle d'un chef d'orchestre.
Sous - domaines :
- acceptation de la commande
- exécution d'ordres
- suivi de l'état des commandes
Système de règlement (facturation)
Contexte : contient toutes les transactions financières. Il fournit une interaction avec le centre de traitement.
Sous - domaines :
- accepter de l'argent pour les commandes
- donner de l'argent aux coursiers pour le travail accompli
Système de statistiques
Contexte : La collecte et le traitement (non la publication) d'informations analytiques.
Sous - domaines :
- statistiques sur les fonds
- statistiques d'application
Système de gestion (panneau de gestion )
Contexte : La publication d'informations analytiques. Boîte à outils des décisions de gestion.
- analyse basée sur les statistiques collectées
- pré-modération des paiements aux coursiers
En fonction des domaines, cartographions-les.
Une carte de domaine (Context map) est un outil graphique qui vous permet de décrire les relations entre les domaines individuels.

La carte montre les liens entre les domaines. Cette carte est très superficielle, mais le sujet n'est pas bien compris. Ceci est la première esquisse, réécriture dont vous obtiendrez le résultat attendu.
La chose la plus importante sur la carte est que nous voyons des connexions entre les domaines. Une telle structure s'intègre très bien dans l'architecture de microservices:
Le principe principal de l'architecture des microservices: faible connectivité et forte adhérence.
Ce principe est donné dans le livre de Sam Newman - Création de microservices , c'est le deuxième livre que vous devrez lire pour commencer à utiliser concrètement les approches décrites dans cet article. Ce que cela signifie: les domaines doivent être faiblement couplés, mais étroitement liés en interne.
La traduction de ces termes est tirée de la traduction officielle russe et reflète peut-être mal le sens transmis. Dans les termes originaux, cela ressemble à: faible couplage (connectivité, engagement, adhérence, conjugaison), haute cohésion (connectivité, résistance).
Pratique de mise en œuvre du partage de domaine
Je voudrais partager mon expérience personnelle - un ensemble de décisions éclairées. Je ne vous invite pas à utiliser ces solutions. Mais ils peuvent être un bon choix si vous ne savez pas par où commencer. Avec une expérience personnelle, les outils seront davantage adaptés à vos besoins.
Les grands principes qui nous ont guidés:
- La simplicité de la solution. Faites des choses complexes simples, pas simples compliquées.
- Pragmatisme Vous devez toujours regarder la situation et, en l’absence de solution existante, en développer une nouvelle. Essayez de tout caractériser, mais évitez le dogmatisme.
- Code! = Documentation. Le code est des instructions pour la machine, la documentation est une instruction pour les personnes. Pas besoin de les confondre et de les distribuer les uns après les autres.
- SOLIDE
Comment implémenter des domaines?
Il est très pratique de sélectionner des domaines en tant que microservices séparés.
Les microservices sont une application distincte qui implémente la logique d'un domaine.
Dans le développement DDD, le principe d'allocation d'un microservice dans une application distincte sera un contexte limité. Cela ne remet pas en cause le principe technique de la séparation des services (si cela est dû à la nécessité d'assurer des performances élevées). Mais le principe contextuel sera dominant et contraignant.
Comment mettre en évidence la relation entre les domaines?
Les relations entre les domaines sont toujours une API. Il peut s'agir d'API json RESTful, gRPC, AMPQ. Dans le cadre de cet article, nous ne comparerons pas un protocole à un autre et mettrons en avant leurs avantages et inconvénients, chacun ayant son propre champ d'application. Mais tout de même, arrêtons-nous sur les recommandations générales:
Soyez flexible dans le choix d'un protocole et rigide dans l'uniformité de sa mise en œuvre.
Choisissez un protocole pour chaque paire de domaines individuellement, n'essayez pas d'utiliser http partout, vous aurez peut-être besoin de files d'attente asynchrones quelque part et les avantages d'AMPQ deviendront évidents pour vous. N'ignorez pas cette opportunité car vous avez RESTful partout.
En revanche, si vous implémentez RESTful json, utilisez une norme de structuration des données. Vous pouvez prendre par exemple jsonapi ou openapi. Si pour une raison quelconque, des solutions toutes faites ne vous conviennent pas et que vous sentez que vous pouvez développer votre norme, la décrire et l'utiliser. Mais utilisez-le partout, ne reproduisez pas les normes "zoo". Si vous devez communiquer avec un système externe où ils ne connaissent rien à vos normes, écrivez un adaptateur de microservice.

Comment implémenter des sous-domaines?
En tant que modules séparés dans un microservice.
Un module est une implémentation d'un sous-domaine, en plaçant la logique dans un espace de noms distinct (espace de noms) au sein d'un microservice unique.
À quoi tout cela ressemble-t-il? Regardons un exemple. Comme nous nous en souvenons, nous avons un domaine de système de livraison - ce domaine a trois sous-domaines:
- inscription par messagerie
- émission de tâches (tâches)
- enregistrement des demandes de retrait des fonds gagnés par le courrier (retrait)
- vérifier que votre microservice fonctionne, outil technique auxiliaire (healt_checker)
Imaginez tout cela sous la forme d'une structure de dossiers:
$ tree --dirsfirst delivery_system delivery_system ├── app/ │ ├── health_checker/ │ │ └── endpoints.rb │ ├── registrations/ │ │ ├── entities/ │ │ ├── forms/ │ │ ├── repositories/ │ │ ├── interactor/ │ │ ├── services/ │ │ ├── validations/ │ │ ├── endpoints.rb │ │ └── helpers.rb │ ├── tasks │ │ ├── entities/ │ │ ├── queries/ │ │ ├── repositories/ │ │ ├── endpoints.rb │ │ └── helpers.rb │ └── withdrawals │ ├── entities/ │ ├── forms/ │ ├── repositories/ │ ├── interactor/ │ ├── services/ │ ├── validations/ │ ├── endpoints.rb │ └── helpers.rb ├── config/ ├── db/ ├── docs/ ├── lib/ │ ├── schemas/ │ └── values/ ├── public ├── specs ├── config.ru ├── Gemfile ├── Gemfile.lock ├── Rakefile └── README.md
Chaque dossier dans le répertoire apps/
implémente l'un ou l'autre sous-domaine, au sein de chaque domaine, il existe différents modèles: entities
, forms
, services
, etc. Nous examinerons en détail chacun des modèles appliqués dans l'un des prochains articles.
Chacun de ces modèles est implémenté dans l'espace de noms correspondant (espace de noms). Par exemple, un formulaire pour créer une demande de paiement à un coursier:
module Withdrawal
Comment implémenter la communication entre les sous-domaines?
Regardons un exemple spécifique. Nous avons un compte de messagerie: Registrations::Entities::Account
. Il fait référence au sous-domaine Registrations
- car nous considérons ce domaine non pas comme un processus d'enregistrement, mais plutôt comme un tableau de compte et un livre d'enregistrement, comme indiqué dans notre documentation commerciale.
Nous avons deux processus dans l'exécution desquels nous accédons à ce compte.
- Créer un compte (inscription)
- Création d'une demande de retrait de fonds gagnés par un coursier (Wihtdrawal)
Comme nous le voyons, ces deux processus appartiennent à des sous-domaines différents - Enregistrement et Wihtdrawal.
module Registrations module Serivices class CreateAccount def call account = Entities::Account.new end end end end module Withdrwals module Serivices class CreateOrder def call account = Registrations::Entities::Account.new end end end end
Dans le premier cas, un appel à la classe sera implémenté via un appel à Entities::Account
. Et dans le second cas, via un appel explicite à Registrations::Entities::Account
. C'est-à-dire si nous spécifions explicitement un sous-domaine, alors la classe provient d'un autre sous-domaine et nous indiquons donc clairement la connexion.
Si la classe ne s'applique explicitement à aucun des sous-domaines, il est logique de la déplacer vers le dossier lib/
. En règle générale, ce sont des classes qui implémentent le modèle «ValueObject». Nous examinerons ce modèle plus en détail dans l'un des articles suivants.
Mise en œuvre à travers le modèle.
Pour citer Eric Evans:
Si l'architecture du programme, ou du moins une partie centrale de celui-ci, ne correspond pas à la structure du modèle de domaine, alors un tel modèle est pratiquement inutile et le bon fonctionnement du programme doit également être remis en question. Dans le même temps, les relations trop complexes entre les modèles et les fonctions dans l'architecture logicielle sont difficiles à comprendre et, en pratique, elles sont difficiles à maintenir à mesure que l'architecture change.
Rappelons l'exemple d'un bon modèle que j'ai déjà cité au début de cet article - une carte topographique. Notre objectif est de pouvoir trouver rapidement la distance entre deux colonies. Nous pourrions utiliser un tableau de référence montrant deux points entre les villes. Et nous pouvons utiliser la carte. Là et là, nous obtenons le même résultat à peu près en même temps. Mais la carte est plus compacte, elle affiche plus précisément le sujet, elle est plus universelle. La carte en tant que modèle est incroyablement expressive. Et si nous la considérons dans le cadre de cette tâche, alors mesurer la distance est plus pratique sur la carte que sur le territoire lui-même, qu'elle reflète. Un modèle qui reflète un domaine peut le surpasser dans certaines propriétés. C'est vraiment incroyable.
La mise en œuvre du modèle est toujours un processus créatif avec des résultats imprévisibles. La qualité de votre code n'est pas sa performance, ni sa complexité, c'est la simplicité et l'expressivité. Améliorez-le grâce à une refactorisation constante, rendez-le flexible et supprimez tout ce qui n'est pas nécessaire. Séparez la couche qui sera responsable de la logique métier du modèle des couches dont le besoin est dû à la mise en œuvre technique. La façon dont nous avons réussi à le faire sera décrite plus loin.