Dans mon pays, vous ne réussirez pas à l'école sans avoir lu comment se plaint le Faust de Goethe, J'ai étudié maintenant la philosophie - Et la jurisprudence, la médecine, - Et même, hélas! Théologie - De bout en bout avec ardeur! - Ici je me tiens, pauvre imbécile.
Malheureusement, aucun de ses efforts et de ses études n’a aidé le médecin à percevoir ce qui tient le monde ensemble dans ses replis intimes..
Et nous voici en informatique: nous avons étudié les langages et les frameworks, les bibliothèques et même - hélas - l’IE! De bout en bout avec ardeur. Mais combien de fois avons-nous concentré nos efforts sur ce qui retient l'application dans son plus profond repli? Le sujet d'aujourd'hui est le domaine des affaires.
La logique métier est parfois considérée comme unique, et c'est par définition! Si la logique métier d'une application ne serait pas unique, il ne serait pas nécessaire d'écrire une application car il existe déjà une solution (à l'exception du moment où une application existe mais n'est pas disponible). Par conséquent, de nombreux développeurs se voient comme des pionniers, pour aller là où aucun homme n'est allé auparavant. Les romantiques mises à part, alors que la logique métier elle-même peut être unique, à un degré remarquable, les techniques pour la mettre en œuvre ne le sont pas. C'est pourquoi les modèles de processus intelligents tels que Rational Unified Process ou Scrum ainsi que des techniques telles que les cycles de développement itératifs et incrémentiels ont été invités. Les architectes de logiciels talentueux ont également élaboré des approches pour la conception de logiciels; parmi eux Eric Evans qui a inventé le terme Conception axée sur le domaine dans son livre avec le même titre.
Les développeurs vont hardiment, là où aucun homme n'est allé.
Je donnerai un aperçu de la manière dont la conception pilotée par le domaine peut influencer le processus de conseil, ainsi que ses concepts de base pour la conception d'un modèle de domaine. Enfin, nous discuterons des besoins en infrastructure nécessaires pour mettre en œuvre un domaine facilement..
Supposons que vous êtes un architecte logiciel pour une application non triviale avec un domaine non trivial, comme le moteur principal d'une grande entreprise de logistique. De nombreuses personnes participent aux discussions sur la planification, parmi lesquelles des chefs de projet, des directeurs de comptes, du marketing, des consultants, etc. Tout le monde n'est pas nécessaire pour faire le travail (je ne partagerai pas mon opinion sur qui cela s'applique), mais deux personnes joueront un rôle crucial dans le processus d'ingénierie des exigences: vous, l'architecte et l'expert du domaine..
UNE Architecte logiciel (du moins dans le contexte commercial) devrait avoir une très bonne compréhension abstraite du fonctionnement des processus, de leur conception et de leur optimisation.
Cela est vrai car les applications métier consistent principalement à concevoir des équivalents numériques efficaces et esthétiques des processus métier. UNE expert de domaine devrait avoir une connaissance approfondie d'un ensemble spécifique de processus, à savoir les processus en cours dans l'entreprise logistique et qui devraient être reflétés par l'application. J'ai constaté que des consultants en entreprise, des directeurs des ventes et des experts en marketing soulignent quelques points positifs et précieux, mais tant que vous n'avez pas quelqu'un dans l'équipe qui s'est mis à la tâche avec des années d'expérience, le projet échouera. Par exemple, votre expert de domaine devrait connaître la largeur de la rampe de chargement au dépôt et s’il existe suffisamment d’espace pour installer un lecteur de codes à barres..
C’est donc vous, spécialisé dans les processus d’affaires numériques, la conception de logiciels et [remplissez vos outils préférés ici], et un expert en logistique possédant des connaissances sur les clients, les employés et la routine quotidienne de l'entreprise. Les chances sont, vous parlez à contre-courant. Domain Driven Design suggère des stratégies pouvant constituer un puissant service de conseil technique. Voilà le mien:
Cela semble amusant! Plongeons dans les détails.
Dans chaque secteur, chaque groupe d’experts a sa propre terminologie. Il est affiné dans chaque entreprise et enrichi de termes spécifiques et de noms de produits. Pensez-y: lorsque des gens comme nous se rencontrent pour un discours de geek sérieux, qui d'autre comprendrait un mot? Il en va de même pour votre domaine et la première chose à faire est de définir un ensemble de termes. Parcourez l'ensemble des processus que le logiciel devrait refléter et écoutez attentivement la façon dont l'expert du domaine le décrit. Tous les termes spécifiques à un domaine doivent être définis de la même manière que les dictionnaires. Vous devez être conscient des mots qui vous semblent familiers mais qui ne sont pas dans le contexte donné. Certaines entreprises n'ont jamais fait ce travail auparavant, même si c'est utile pour d'autres domaines.
Créez un glossaire dédié de vos termes omniprésents, assurez-vous qu'il est approuvé par le client et facturez le processus de consultation! Un glossaire peut ressembler à ceci:
Un extrait d'un glossaire.Remarquez comment un glossaire bien défini définit déjà des dépendances et des associations. Comme le ordre, qui accueille plusieurs articles. Vous aurez sûrement des cours pour ceux de votre logique métier! Votre Ordre
la classe aura probablement une méthode comme getItems ()
. Sans prendre en compte les techniques de programmation, un glossaire peut préparer le terrain pour votre modèle de domaine! Parallèlement à cela, vous construisez un langage qui est utilisé tout au long du projet: dans les mails, dans les réunions et certainement dans le code! Votre code doit refléter le domaine. par conséquent, il doit être défini dans le glossaire. Voici une règle de base: chaque fois que vous créez une classe qui ne porte pas le nom d'une entrée de votre glossaire, votre langue omniprésente peut ne pas encore être définie suffisamment!
Sous le couvert de l'obscurité, nous avons modifié la vue sur les exigences! Normalement, un client décrit ce que doit faire le logiciel à écrire. Une description typique pourrait être: "Nous avons besoin d'un moyen d'ajouter des notes à un client et de les imprimer." C'est un bon point de départ, mais il ne se concentre pas sur le domaine commercial. Il introduit une sorte d’interface utilisateur, une fonctionnalité d’impression, et plus encore. Vous en aurez sûrement besoin dans votre application, mais cela ne fait pas partie du domaine. Domain Driven Design se concentre sur la modélisation du véritable objectif d'une application: le domaine métier.
Tout le reste devrait sortir de là, une fois le domaine terminé. Le commutateur est le suivant: n'implémentez pas de processus et créez un domaine pur qui reflète les besoins des clients en objets. Voici un moyen de visualiser la description du client supérieur (les accesseurs et les setters ne sont ajoutés que lorsque cela est nécessaire pour la compréhension):
Un diagramme simple qui reflète le domaine requis.Nous avons maintenant un ensemble de classes et d’associations qui ne font que refléter les définitions de notre glossaire. Ce modèle est-il capable d'effectuer les tâches nécessaires? Sûr! Vous aurez besoin d'un PrinterService
et une interface utilisateur quelque part dans votre application, mais ils ont juste besoin de récupérer des données du domaine. Ils ne sont pas nécessaires pour le moment et sa mise en œuvre ne sera pas déterminante pour le résultat.
La philosophie de DDD repose sur l'hypothèse selon laquelle une couche de domaine soigneusement conçue peut exécuter facilement tous les processus nécessaires. Un modèle de domaine est évolutif, car il n'est pas conçu pour satisfaire une tâche donnée, il est conçu pour refléter un concept d'entreprise. Il est interchangeable, car il n'est lié à aucun logiciel spécifique, pas même à une interface utilisateur. Vous pouvez utiliser le même modèle dans le lecteur de code à barres sur la rampe de chargement du dépôt! Comme nous le verrons au chapitre suivant, il n'est même pas lié aux autres composants qui construisent votre application..
Dans un de mes articles récents, j'ai écrit sur l'application du principe KISS.
Dans l'un de mes articles récents, j'ai écrit sur l'application du principe KISS: la plupart des systèmes fonctionnent mieux s'ils restent simples plutôt que compliqués. Eh bien, lorsqu'il s'agit de mettre en œuvre un domaine basé sur la philosophie de DDD, vous pouvez rencontrer une approche plutôt radicale dans le monde moderne des cadres, des schémas et des disciplines; tels que, implémenter un objet ordinaire dans le langage clair de votre choix. Aucune dépendance de structure, aucune convention de bibliothèque, aucune trace d'aucune API, aucun nom de fantaisie. Juste un objet ancien et simple (puisqu’un concept n’est pas pris au sérieux sans un nom de fantaisie dans le monde de Java, ils en ont reçu un là-bas).
Lorsque nous voulons refléter le modèle de domaine, un point crucial est de définir son état. En programmation orientée objet, l'état d'un objet est défini par l'état de ses propriétés. De même, l'état du modèle de domaine est défini par l'état de ses objets. Par conséquent, nous devons avoir un moyen de définir clairement l’état des objets. Si nous ne pouvions pas faire cela, nous échouerions à des cas d'utilisation simples comme «Combien y a-t-il d'ordres?», Car la réponse nécessite toujours une connaissance de l'état de tous Ordre
des objets dans un domaine et un moyen de les identifier et de les distinguer. DDD définit deux types d’objets: les entités et les objets de valeur..
Un entité est un concept familier si vous connaissez les bases de données relationnelles.
Les tables d'une base de données relationnelle ont généralement un identifiant unique qui distingue une ligne d'une autre. La même chose est vraie pour les entités. Une entité doit avoir un identificateur clair unique dans l’ensemble du système. Pour une commande, cela pourrait être une propriété du type uint, nommée numéro de commande
. Bien sûr, vous examinerez votre glossaire, où le terme correct doit être défini.
Une entité reste la même lorsque certaines propriétés changent. Par exemple, vous pouvez ajouter ou supprimer des éléments d'une commande, mais ce serait la même commande. Que se passe-t-il lorsque vous changez la numéro de commande
? Eh bien, à partir du POV de votre domaine, une commande est supprimée tandis qu'une autre est créée..
UNE objet de valeur est un simple conteneur d'informations. C'est immuable une fois créé. Changer une propriété signifie que vous changeriez l'objet de valeur. Un objet de valeur est défini par toutes ses propriétés. il n'a pas besoin d'identifiant unique. L'objet entier est un. Un exemple d'objet de valeur serait un OrderAddress
, tel qu'il est défini par le nom, l'adresse et la ville du destinataire. Si vous changez une propriété, par exemple la ville, OrderAddress changera complètement.
Il est important de diviser les objets en objets de valeur et en entités pour définir l’état de votre domaine. Il s’agit du travail de base pour identifier les composants. Mais il est également important de les définir pour avoir un domaine évolutif et maintenable. Les entités sont la représentation d'objets du monde réel tels que des personnes, des ordres ou des objets. Les objets de valeur sont des conteneurs contenant des informations telles que des couleurs ou des adresses. Ils sont réutilisables et peuvent être partagés entre entités, voire l'ensemble du système. Leur définition peut prendre un peu de pratique, car cela dépend du cas d'utilisation, qu'il s'agisse d'un objet de valeur ou d'une entité..
Lorsque nous examinons l’abrégé de notre glossaire, nous pouvons voir les connexions et les dépendances entre nos objets dans la couche de domaine. Dans DDD, cela s'appelle les associations et c'est le modèle des interactions qui ont lieu.
Par exemple, les articles font partie de la commande. Si nous procédions par rapport à une base de données relationnelle, il s’agirait d’une relation un à plusieurs (ou 1: n). Si chaque commande avait exactement une adresse de commande, il s'agirait d'une relation un à un. Puisque nous ne nous soucions pas des bases de données relationnelles et que nous nous soucions seulement de la finition du domaine, la relation peut être facilement exprimée avec deux méthodes dans la classe Order: getItems ()
et getOrderAddress ()
. Notez que le premier est pluriel (il y a beaucoup d'éléments) et le second est singulier. Si vous aviez une relation plusieurs-à-plusieurs, vous donneriez une méthode getter aux deux classes. Bien sûr, vous avez besoin de setters aussi - je les ai omis pour garder les exemples légers.
Dans DDD, nous essayons d'éviter les relations plusieurs à plusieurs, car elles ont tendance à ajouter de la complexité au domaine. Techniquement, cela signifie que deux objets doivent être synchronisés au cours de leur cycle de vie et que le fait de les synchroniser peut conduire à une violation du principe DRY. C'est pourquoi le processus de raffinement du modèle doit viser la simplicité. Souvent, une association est plus forte dans un sens que dans l'autre et il est judicieux de redéfinir la structure en une relation un à plusieurs. Vérifiez si l'association est pertinente pour la logique métier de votre application. Si cela se produit uniquement dans des cas d'utilisation non essentiels et rares, vous pouvez rechercher un autre moyen de recevoir les informations nécessaires..
Les associations construisent un arbre d'objets et vous devriez vous retrouver avec une construction d'association dans laquelle chaque objet peut être récupéré à l'aide de méthodes getter. Il s'agit d'une construction parent-enfant qui mène finalement à un objet racine. C'est appelé agrégation en DDD. Une bonne conception conduit finalement à un agrégat capable de refléter l'ensemble du domaine. Pour l'instant, nous n'avons examiné qu'une petite partie de notre glossaire, mais il semble qu'un client soit notre agrégat racine:
L'objet client est le parent de tous les membres du domaine.Les agrégats jouent un rôle important, car DDD tente d'isoler le domaine de l'application environnante. Si nous aimons avoir des informations sur un client, nous demandons un agrégat racine et pouvons parcourir ses enfants pour accéder aux informations via une interface claire de getters et de setters..
DDD est comme les ventes, il fournit un visage au client, l’agrégat au système environnant. Par conséquent, il donne accès à un ensemble structuré de processus, d’informations et de méthodes pertinentes; par exemple la commande.
Domain Driven Design est comme les ventes, il fournit un visage au client.
L'application environnante accède à un agrégat par le biais de référentiels, qui sont essentiellement une sorte de façade. En d'autres termes: un objet de domaine est un agrégat s'il dispose d'un référentiel. Les référentiels fournissent des méthodes pour rechercher des agrégats. Des exemples peuvent être findClientByEmail (chaîne email)
ou juste Trouver tout()
. Ils effectuent également des mises à jour et ajoutent de nouveaux objets au domaine. Ainsi, ils ont probablement des méthodes comme add (client newClient)
ou delete (Client toBeDeletedClient)
.
Avec un agrégat, vous accédez aux enfants uniquement par l'intermédiaire de son parent. Par exemple, un agrégat client vous donne accès à toutes les commandes du client. Mais si vous devez accéder aux données sous un angle différent de celui du client, vous pouvez établir un deuxième agrégat. Supposons que vous souhaitiez obtenir une liste de toutes les commandes, quel que soit le client par lequel elles ont été passées. Un référentiel de commandes fera le travail!
La couche de domaine et ses référentiels.Comme le référentiel est le point d'entrée de l'application environnante dans la couche de domaine, c'est le lieu où d'autres joueurs entrent dans la zone. Rappelez-vous que nous avons affaire à des objets simples pour l'instant.
Vous êtes-vous demandé comment cela va devenir réel? C'est là que l'infrastructure entre en jeu. DDD est un excellent compagnon pour les frameworks, car il est construit sur des objets simples, avec un schéma simple d'objets de valeur, d'entités et d'agrégats. Cependant, comme la simplicité est synonyme de puissance en informatique, nous sommes maintenant en mesure d'externaliser toute la partie infrastructure. Regardons sous le capot et comment DDD peut se propager autour d'une application.
Vous avez peut-être remarqué que notre attention sur le domaine excluait la couche de persistance ainsi que les éléments courants tels que les vues ou les contrôleurs de notre liste de tâches. L’ensemble de l’application peut consister en des éléments beaucoup plus complexes que de simples objets ordinaires, et je tiens à souligner quelques étapes à suivre pour associer le domaine et l’application, ainsi que les stratégies de mise en œuvre existantes. Je vais donner quelques exemples basés sur FLOW3, un cadre d’application dont l’objectif principal est de fournir une infrastructure DDD. Ce n'est pas nécessaire, mais cela ne fera pas de mal si vous lisez mon introduction. Pour appliquer le domaine métier à une application, les étapes suivantes sont courantes:
Lorsque vous consultez les commentaires sur l’article, Programmation Orientée Aspect (AOP), vous verrez une discussion intéressante sur la question de savoir si un framework doit ou non ajouter son empreinte via des annotations de commentaires. L’approche de FLOW3 est basée sur la manière dont la conception dirigée par le domaine est mise en œuvre. Regardez ce code:
/ ** * Un client * * @ FLOW3 \ Scope ("prototype") * @ FLOW3 \ Entity * / class Client / ** * Nom du client. * * @ FLOW3 \ Validate (type = "Text") * @ FLOW3 \ Validate (type = "StringLength", options = "minimum" = 1, "maximum" = 80) * @ORM \ Colonne (longueur = 80 ) * Chaîne @var * / protected $ name; / ** * Récupère le nom du client * * @ chaîne de retour Le nom du client * / fonction publique getName () return $ this-> Nom; / ** * Définit le nom de ce client * * @ chaîne_param $ Nom Le nom du client * @return void * / public function nomSet ($ name) $ this-> name = $ name;
Cette classe est très simple et ne contient pas beaucoup de logique métier, mais cela changera probablement une fois l'application développée. FLOW3 est présent à travers des annotations de code. Il définit la classe comme un entité et ajoute quelques règles de validation à appliquer (optionnel). Notez qu'il existe une annotation nommée @ORM \ Column (length = 80)
. Ceci est une information pour la couche de persistance et nous y reviendrons dans un instant.
FLOW3 utilise des annotations ici pour maintenir le domaine propre. Vous êtes libre d'utiliser la classe n'importe où ailleurs, car il s'agit toujours d'un objet simple. Vous pouvez choisir de passer à la symfony framework, qui utilise la même couche de persistance (Doctrine), le code fonctionnerait donc presque immédiatement. En poussant la configuration du framework hors du champ de l'interpréteur PHP, le domaine reste un vieil objet PHP ordinaire. Vous pouvez le réutiliser même sans aucun cadre.
Mais maintenant que l'infrastructure est consciente de l'objet, elle peut maintenant calculer les exigences pour une table de base de données MySQL. Afin de stocker les instances du client de classe, FLOW3 (et Doctrine en tant que structure de persistance) effectuerait les opérations suivantes à votre place:
La définition de la propriété pour les articles de notre commande peut ressembler à ceci:
/** * Les objets. * * @ORM \ OneToMany (mappedBy = "order") * @ORM \ OrderBy ("price" = "ASC") * @var \ Doctrine \ Common \ Collections \ Collection<\LogisticApp\Domain\Model\Item> * / items protégés $;
Notez que cela retourne une collection Doctrine, qui est une sorte de wrapper pour un tableau, comme ArrayLists en Java. Cela signifie essentiellement que tous les éléments doivent être du type donné, en l'occurrence Item. J'ai choisi d'ajouter un bon de commande sur la manière dont je veux que la collection soit organisée (en fonction du prix des articles).
La contrepartie dans la classe Item pourrait être:
/** * L'ordre. * * @ORM \ ManyToOne (inversedBy = "items") * @var \ LogisticApp \ Domaine \ Modèle \ Order * / protected $ order;
Ce n'est que la pointe de l'iceberg, mais il devrait vous donner une idée de la façon dont les choses peuvent être automatisées: Doctrine fournit une stratégie puissante sur la façon de mapper des associations sur les tables où il stocke l'objet. Par exemple, étant donné que les éléments se traduiraient par une relation un-à-plusieurs (une commande peut avoir plusieurs éléments) dans la base de données, Doctrine ajouterait silencieusement une clé étrangère pour la commande à la table des éléments. Si vous décidez d'ajouter un référentiel pour l'élément (en le transformant en agrégat), vous pourrez accéder par magie à un findByOrder (Ordre de commande)
méthode. C'est la raison pour laquelle nous ne nous sommes pas souciés des bases de données ou de la persistance lors de la création de domaine. C'est quelque chose qu'un framework peut gérer..
Si vous débutez dans les frameworks de persistance, la méthode de mappage des objets vers une base de données relationnelle est appelée ORM (Cartographie relationnelle d'objet). Il présente des inconvénients en termes de performances, principalement dus aux différentes approches des bases de données relationnelles et du modèle objet. Il y a de longues discussions à ce sujet. Toutefois, dans les applications CRUD modernes (et pas uniquement pilotées par domaine), ORM est le moyen à privilégier, principalement pour des raisons de maintenance et d’extensibilité. Cependant, vous devez connaître votre ORM et bien comprendre son fonctionnement. Ne pensez pas que vous n'avez plus besoin de connaissances sur les bases de données!
Comme vous l'avez peut-être remarqué, vos objets peuvent être complexes et avoir une longue ligne de traversée s'ils ont de nombreux enfants, lesquels ont eux-mêmes plusieurs enfants..
Par conséquent, une fois que le référentiel récupère les données d'une base de données, celles-ci doivent être transformées en objets de manière intelligente. Puisque nous avons maintenant une couche de persistance impliquée, la transformation est beaucoup plus complexe que la simple instanciation d'un objet. Nous devons gérer la ligne de traversée en réduisant au minimum les appels pertinents à la base de données. Tous les enfants ne sont pas toujours nécessaires, ils peuvent donc être récupérés à la demande.
Certains objets seront des objets de valeur qui ne doivent être créés qu'une seule fois, ce qui permet d'économiser beaucoup de mémoire. C'est pourquoi toute couche de domaine nécessite une fabrique intelligente qui génère les objets pour vous. Par conséquent, dans les cadres modernes, la Nouveau
l’opérateur est considéré comme trop bas pour les applications modernes. FLOW3 contribue grandement à l’instanciation des objets avec le logiciel Nouveau
mot-clé, mais la compilation en arrière-plan change automatiquement la création d’un objet brut en une puissante gestion d’objets Certaines fonctionnalités que votre gestionnaire d'objet / fabrique devrait être capable de gérer, quel que soit le framework utilisé, sont:
Vous avez peut-être mal vu la dernière phrase. Tout au long de l'article, j'ai insisté sur l'utilisation d'objets simples dans le domaine. J'ai même violé le paradigme «ne vous répétez pas» et je l'ai mentionné à plusieurs reprises car il est très important pour DDD. Et maintenant, je vous dis que vous avez des dépendances et des services qui doivent faire partie de votre domaine…
Il existe une triste vérité dans le monde réel: il n’existe pas de domaine pur. Vous ne rencontrerez presque jamais un client qui repart de zéro; alors, vous devez satisfaire à des circonstances telles que les systèmes existants. Leur mise en œuvre est peut-être horrible, mais la société ne peut pas s'en débarrasser. Vous devrez peut-être appeler des services et des API et extraire des données de divers tiers, et ces systèmes hérités influencent le domaine métier..
Tout ce dont nous avons discuté jusqu'ici est important, mais la question de savoir comment un framework résout la dépendance à des services autres que des domaines est essentielle à une conception propre axée sur le domaine. C’est la raison pour laquelle l’équipe FLOW3 a consacré d’énormes efforts à la mise en œuvre de l’aspect orienté; c'est un moyen d'introduire des services dans le domaine sans toucher au code sans enfreindre la règle des vieux objets. Il existe d'autres approches, telles que le déplacement des dépendances entre les services et le domaine vers le contrôleur, mais la programmation orientée aspect est de loin la manière la plus élégante que je connaisse. J'aimerais entendre vos commentaires sur ce sujet!
Un bon cadre peut vous apporter beaucoup de soutien en plus des points que j'ai mentionnés. Par exemple, FLOW3 transfère de manière transparente les objets de domaine dans la vue avec son moteur de templates extrêmement cool appelé Fluid. L'écriture de modèles Fluid, une fois le domaine terminé, est aussi relaxante qu'une journée à la plage.
Cet article est juste une introduction à la conception dirigée par le domaine. J'ai présenté certains des concepts de base, mais je dois admettre qu'il peut être difficile de comprendre la théorie seule. Je tiens à vous encourager à essayer Domain Driven Design pour votre propre compte dans un projet réel. Vous constaterez que les concepts de domaine sont très intuitifs à utiliser.
J'ai été projeté dans DDD comme un poisson hors de l'eau dans un très grand projet extbase, sans grande connaissance préalable des concepts (extbase est un cadre de conception piloté par domaine pour la construction d'extensions pour le CMS Typo3 basé sur FLOW3). Il a élargi ma vision de la conception de logiciels et j'espère que cela élargira également le vôtre..