Où est la limite entre la délégation et l'encapsulation de la logique métier? Il me semble que plus nous déléguons, plus nous devenons anémiques . Cependant, la délégation encourage également la réutilisation et le principe de DRY. Alors, qu'est-ce qui convient de déléguer et que devrait rester dans nos modèles de domaine?
Prenons comme exemple les préoccupations suivantes:
Autorisation . L'objet de domaine doit-il être responsable de la gestion de ses règles de contrôle d'accès (comme une propriété CanEdit) ou doit-il être délégué à un autre composant / service uniquement responsable de la gestion de l'accès, par exemple IAuthorizationService.CanEdit (objet)? Ou devrait-il être une combinaison des deux? Peut-être que l'objet de domaine a une propriété CanEdit qui délègue à un IAuthorizationService interne pour effectuer le travail réel?
Validation . La même discussion que ci-dessus concerne la validation. Qui maintient les règles et qui est chargé de les évaluer? D'une part, l'état de l'objet doit appartenir à cet objet et la validité est un état, mais nous ne souhaitons pas réécrire le code utilisé pour évaluer les règles de chaque objet du domaine. Nous pourrions utiliser l'héritage dans ce cas ...
Création d'objet . Classe d'usine contre méthodes d'usine contre "nouveauté" d'une instance. Si nous utilisons une classe de fabrique séparée, nous pouvons isoler et encapsuler la logique de création, mais aux dépens de l'ouverture de l'état de notre objet à la fabrique. Cela peut être géré si notre couche de domaine se trouve dans un assemblage séparé en exposant un constructeur interne utilisé par l'usine, mais cela devient un problème s'il existe plusieurs modèles de création. Et si l’usine n’appelle que le bon constructeur, à quoi sert-elle d’avoir l’usine?
Les méthodes d'usine de la classe éliminent le problème d'ouverture de l'état interne de l'objet, mais comme elles sont statiques, nous ne pouvons pas rompre les dépendances en injectant une interface de fabrique comme avec une classe de fabrique distincte.
Persistance . On pourrait argumenter que si notre objet domaine doit exposer CanEdit tout en déléguant la responsabilité d'effectuer le contrôle des autorisations à une autre partie (IAuthorizationService), pourquoi ne pas avoir une méthode Save sur notre objet domaine qui répète les mêmes choses? Cela nous permettrait d'évaluer l'état interne de l'objet pour déterminer si l'opération peut être effectuée sans rompre l'encapsulation. Bien sûr, cela nécessite que nous injections l'instance de référentiel dans notre objet de domaine, ce qui sent un peu pour moi. Alors, levons-nous un événement de domaine à la place et permettons à un gestionnaire d'effectuer l'opération de persistance?
Vous voyez où je vais avec ça?
Rockford Lhotka a eu une discussion intéressante sur les raisons pour lesquelles il a choisi la voie de la classe responsable pour son framework CSLA. J'ai un peu d'histoire avec ce cadre et je peux voir son idée d'objets métier en parallèle avec des objets de domaine de plusieurs manières. Mais pour essayer de devenir plus adhérent aux bons idéaux DDD, je me demande quand la collaboration devient trop lourde.
Si je me retrouve avec IAuthorizationService, IValidator, IFactory et IRepository pour ma racine globale, que reste-t-il? Avoir une méthode Publish qui modifie l'état de l'objet de Brouillon à Publié est-il suffisant pour considérer la classe comme un objet de domaine non-anémique?
Tes pensées?
la source
Réponses:
La plupart des confusions semblent concerner des fonctionnalités qui ne devraient absolument pas exister dans le modèle de domaine:
La persistance ne devrait jamais être dans le modèle de domaine. Plus jamais. C'est la raison pour laquelle vous utilisez des types abstraits, par exemple
IRepository
si une partie du modèle doit faire quelque chose comme récupérer une partie différente du modèle et utiliser l'injection de dépendance ou une technique similaire pour câbler la mise en œuvre. Alors rayez ça du compte rendu.L’autorisation ne fait généralement pas partie de votre modèle de domaine, sauf s’il s’agit bien du domaine, par exemple si vous écrivez un logiciel de sécurité. Les mécanismes permettant de savoir qui est autorisé à exécuter quoi dans une application sont normalement gérés à la "périphérie" du niveau métier / domaine, les parties publiques auxquelles les éléments d'interface utilisateur et d'intégration sont réellement autorisés à parler - le contrôleur dans MVC, les services ou le système de messagerie lui-même dans une SOA ... vous obtenez l'image.
Les usines (et je suppose que vous voulez dire des usines abstraites ici) ne sont pas vraiment mauvaises à avoir dans un modèle de domaine, mais elles sont presque toujours inutiles. Normalement, vous n’avez une usine que lorsque les mécanismes internes de la création d’objet peuvent changer. Mais vous n'avez qu'une implémentation du modèle de domaine, ce qui signifie qu'il n'y aura jamais qu'un seul type d'usine qui invoque toujours les mêmes constructeurs et un autre code d'initialisation.
Vous pouvez avoir des usines "de commodité" si vous le souhaitez - des classes qui encapsulent des combinaisons courantes de paramètres de constructeur - mais honnêtement, d'une manière générale, si vous avez beaucoup d'usines dans votre modèle de domaine, vous ne faites que gaspiller des lignes. de code.
Donc, une fois que vous avez résolu tous ces problèmes, il ne reste plus qu’une validation. C'est le seul qui soit un peu délicat.
La validation fait partie de votre modèle de domaine, mais elle fait également partie de tous les autres composants de l'application. Votre interface utilisateur et votre base de données auront leurs propres règles de validation, similaires mais différentes, basées sur un modèle conceptuel similaire mais différent. Il n'est pas vraiment spécifié si les objets doivent ou non avoir une
Validate
méthode, mais même s'ils le font, ils la délèguent généralement à une classe de validateur (pas d'interface - la validation n'est pas abstraite dans le modèle de domaine, elle est fondamentale).Gardez à l'esprit que le validateur fait encore techniquement partie du modèle; il n'a pas besoin d'être attaché à une racine agrégée car il ne contient aucune donnée ni aucun état. Les modèles de domaine sont des éléments conceptuels, traduisant généralement physiquement en un assemblage ou une collection d'assemblages. Ne vous inquiétez pas du problème "anémique" si votre code de délégation réside à proximité immédiate du modèle objet; ça compte toujours.
Ce que tout cela vient vraiment à dire que si vous allez faire DDD, vous devez comprendre ce que le domaine est . Si vous parlez encore de choses comme la persistance et l'autorisation, vous êtes sur la mauvaise voie. Le domaine représente l'état de fonctionnement d'un système - les objets et attributs physiques et conceptuels. Tout ce qui ne concerne pas directement les objets et les relations elles-mêmes n'appartient pas au modèle de domaine, point à la ligne.
En règle générale, pour déterminer si quelque chose appartient ou non au modèle de domaine, posez-vous la question suivante:
"Cette fonctionnalité peut-elle jamais changer pour des raisons purement techniques?" En d'autres termes, pas en raison d'un changement observable dans le monde réel des affaires ou du domaine?
Si la réponse est "oui", alors il n'appartient pas au modèle de domaine. Cela ne fait pas partie du domaine.
Il y a de fortes chances que vous changiez un jour vos infrastructures de persistance et d'autorisation. Par conséquent, ils ne font pas partie du domaine, ils font partie de l'application. Ceci s’applique également aux algorithmes, comme le tri et la recherche; vous ne devriez pas aller implémenter une implémentation de code de recherche binaire dans votre modèle de domaine, car votre domaine ne concerne que le concept abstrait de recherche, pas son fonctionnement.
Si, après avoir éliminé tout ce qui n'a pas d'importance, vous trouvez que le modèle de domaine est vraiment anémique , alors cela devrait être une bonne indication que DDD est tout simplement le paradigme faux pour votre projet.
Certains domaines sont vraiment anémiques. Les applications de bookmarking social n'ont pas vraiment de "domaine" à proprement parler; tous vos objets sont fondamentalement juste des données sans fonctionnalité. Par contre, un système de vente et de gestion de la relation client a un domaine assez lourd. lorsque vous chargez une
Rate
entité, il est raisonnable de penser que vous pouvez réellement utiliser ce taux, par exemple l'appliquer à une quantité d'ordre et lui demander de déterminer les remises sur volume, les codes promotionnels et tout ce qui est amusant.Objets de domaine qui vient contiennent des données en général ne signifie pas que vous avez un modèle de domaine anémique, mais cela ne veut pas nécessairement dire que vous avez créé une mauvaise conception - il pourrait juste dire que le domaine lui - même est anémique et que vous devriez utiliser un méthodologie différente.
la source
Non. L'autorisation est une préoccupation en soi. Les commandes qui ne seraient pas valables faute d'autorisations devraient être rejetées avant le domaine, le plus tôt possible - ce qui signifie souvent que nous voudrons même vérifier l'autorisation d'une commande potentielle afin de construire l'interface utilisateur (afin de ne pas même montrer à l'utilisateur l'option de l'édition).
Le partage de stratégies d'autorisation entre les couches (dans l'interface utilisateur et plus loin dans un gestionnaire de service ou de commande) est plus facile lorsque l'autorisation est séparée du modèle de domaine.
L’autorisation contextuelle est un problème difficile à résoudre: une commande peut être autorisée ou non, non seulement en fonction de rôles d’utilisateur mais également de données / règles commerciales.
Je dirais aussi non, pas dans le domaine (principalement). La validation se produit dans différents contextes et les règles de validation diffèrent souvent entre les contextes. Il existe rarement un sens simple et absolu de validité ou d'invalidité lorsque l'on considère les données encapsulées par un agrégat.
De même, comme pour l'autorisation, nous utilisons la logique de validation sur plusieurs couches - dans l'interface utilisateur, dans le gestionnaire de services ou de commandes, etc. Encore une fois, il est plus facile d'utiliser DRY avec validation s'il s'agit d'un composant séparé. D'un point de vue pratique, la validation (en particulier lors de l'utilisation de frameworks) nécessite d'exposer des données qui devraient autrement être encapsulées et il est souvent nécessaire d'attacher des attributs personnalisés aux champs et aux propriétés. Je préfère de loin que ceux-ci soient sur d'autres classes que mes modèles de domaine.
Je préférerais dupliquer certaines propriétés dans quelques classes similaires plutôt que d'essayer d'imposer les exigences relatives à un cadre de validation à mes entités. Cela finit inévitablement par semer le chaos dans les classes d'entités.
J'utilise une couche d'indirection. Dans mes projets plus récents, il s’agit d’une commande + gestionnaire pour créer quelque chose, c’est-à-dire
CreateNewAccountCommand
. Une alternative pourrait consister à toujours utiliser une fabrique (bien que cela puisse être gênant si le reste d'une opération d'entités est exposé par une classe de service distincte de la classe de fabrique).En général, cependant, j'essaie d'être plus flexible avec les choix de conception pour la création d'objets.
new
est facile et familier mais pas toujours suffisant. Je pense qu’il est important de faire preuve de jugement et de permettre à différentes parties d’un système d’utiliser différentes stratégies au besoin.C'est rarement une bonne idée. Je pense qu'il y a beaucoup d'expérience partagée pour soutenir cela.
Peut-être qu'un modèle de domaine n'est pas le bon choix pour cette partie de votre application.
la source
OK, voilà pour moi. Je préviens cela en disant que:
Une optimisation prématurée (y compris la conception) peut souvent poser problème.
IANMF (je ne suis pas Martin Fowler);)
Un sale petit secret réside dans le fait que, dans les projets de petite taille (même sans doute de taille moyenne), c'est la cohérence de votre approche qui importera.
Autorisation
Pour moi, l'authentification et l'autorisation constituent toujours une préoccupation transversale. Dans mon joyeux petit monde Java, cela est délégué à la sécurité Spring ou au framework Apache Shiro.
Validation Pour moi, la validation fait partie de l'objet, car je la vois comme définissant ce qu'est l'objet.
Par exemple, un objet de voiture a 4 roues (OK il y a quelques exceptions étranges, mais ignorons la voiture étrange à 3 roues pour le moment). Une voiture n'est tout simplement valide que si elle en a 4 (dans mon monde), de sorte que la validation fait partie de la définition d'une voiture. Cela ne signifie pas que vous ne pouvez pas avoir de classes de validation auxiliaires.
Dans mon monde Java heureux, j'utilise des cadres de validation Bean et des annotations simples sur la plupart de mes champs Bean. Il est alors facile de valider votre objet, peu importe la couche dans laquelle vous vous trouvez.
Création d'objet
Je considère les cours d'usine avec prudence. Trop souvent j'ai vu la
xyxFactoryFactory
classe;)J'ai tendance à simplement créer un
new
objet selon les besoins, jusqu'à ce que je rencontre un cas où l'injection de dépendance est justifiée (et puisque j'essaie de suivre une approche TDD, cela se produit plus souvent).Dans mon joyeux monde Java c'est de plus en plus Guice, mais du printemps est toujours le roi ici.
Persistance
Il s’agit donc d’un débat qui tourne en rond et en rond-point et j’ai toujours deux idées à ce sujet.
Certains disent que si vous regardez un objet de manière "pure", la persistance n'est pas une propriété fondamentale, mais simplement une préoccupation extérieure.
D'autres pensent que vos objets de domaine implémentent implicitement une interface "persistante" (ouais, je sais que j'étire ici). Par conséquent, il est bon d’avoir les différentes méthodes
save
,delete
etc. Ceci est considéré comme une approche pragmatique et de nombreuses technologies ORM (JPA dans mon heureux monde Java) traitent les objets de cette manière.Pour des raisons de sécurité transversales, je m'assure que les autorisations modifier / supprimer / ajouter / quels que soient ce paramètre sont correctement définies sur le service qui appelle la méthode save / update / delete sur l'objet. Si je suis vraiment paranoïaque, je pourrais même définir les autorisations sur l'objet de domaine lui-même.
HTH!
la source
Jimmy Nilsson aborde ce sujet dans son livre sur le DDD. Il a commencé avec un modèle anémique, a adopté des modèles non anémiques pour un projet ultérieur et a finalement opté pour des modèles anémiques. Son raisonnement était que les modèles anémiques pourraient être réutilisés dans plusieurs services avec une logique métier différente.
Le compromis est le manque de capacité de découverte. Les méthodes que vous pouvez utiliser pour exploiter vos modèles anémiques sont réparties dans un ensemble de services situés ailleurs.
la source
Cette question a été posée il y a longtemps, mais elle est étiquetée avec Domain Driven Design. Je pense que la question elle-même contient un malentendu fondamental de toute la pratique et que les réponses, y compris la réponse acceptée, perpétuent un malentendu fondamental.
Il n'y a pas de "modèle de domaine" dans une architecture DDD.
Prenons l'autorisation à titre d'exemple. Permettez-moi de vous demander de réfléchir à une question: imaginez deux utilisateurs différents s'authentifier sur votre système. Un utilisateur a la permission de changer une certaine entité, mais l'autre pas. Pourquoi pas?
Je déteste les exemples simples et artificiels parce qu'ils confondent souvent plus qu'ils n'éclairent. Mais supposons que nous ayons deux domaines différents. First est une plate-forme CMS pour une agence de marketing. Cette agence a de nombreux clients qui ont tous un contenu en ligne qui doit être géré par des rédacteurs et des graphistes. Le contenu comprend des articles de blog ainsi que des pages de renvoi pour différents clients.
L'autre domaine est la gestion des stocks pour une entreprise de chaussures. Le système gère les stocks à partir du moment où ils arrivent du fabricant en France, dans les centres de distribution situés aux États-Unis continentaux, dans les magasins de détail sur les marchés locaux et enfin chez le client qui achète les chaussures au détail.
Si vous pensez que les règles d'autorisation sont les mêmes pour les deux sociétés, alors, ce serait un bon candidat pour un service en dehors du domaine. Mais je doute que les règles d'autorisation soient les mêmes. Même les concepts derrière les utilisateurs seraient différents. Le langage serait certainement différent. L'agence de marketing a probablement des rôles de post-auteur et de propriétaire d'actifs, tandis que la société de chaussures a probablement des rôles de commis aux expéditions, de gestionnaire d'entrepôt ou de magasin.
Ces concepts ont probablement toutes sortes de règles de permission associées qui doivent être modélisées dans le domaine. Mais cela ne signifie pas qu'ils font tous partie du même modèle, même au sein de la même application. Parce que rappelez-vous qu'il existe différents contextes délimités.
Donc, on pourrait peut-être considérer qu'un modèle de domaine non anémique dans le contexte de l'autorisation diffère du contexte d'acheminement des expéditions vers des magasins ayant un faible inventaire ou d'acheminer les visiteurs du site vers la page de destination appropriée en fonction de l'annonce sur laquelle ils ont cliqué.
Si vous vous retrouvez avec des modèles de domaine anémique, vous devrez peut-être simplement consacrer plus de temps à la cartographie de contexte avant de commencer à écrire du code.
la source