Modèle de domaine anémique: avantages / inconvénients

J’aimerais savoir quels sont les avantages et les inconvénients de l’utilisation d’un modèle de domaine anémique (voir le lien ci-dessous).

Article Fowler

Les pros:

  • Vous pouvez prétendre que c’est un modèle de domaine et se vanter auprès de vos amis développeurs et le mettre sur votre CV.
  • Il est facile de générer automatiquement à partir de tables de firebase database.
  • Il correspond étonnamment bien aux objects de transfert de données.

Les inconvénients:

  • Votre logique de domaine existe ailleurs, probablement dans une classe remplie de méthodes de classe (statique). Ou votre code graphique. Ou à plusieurs endroits, tous avec une logique contradictoire.
  • C’est un anti-pattern, donc les autres développeurs vous demanderont si vous comprenez les concepts de la conception orientée object.

Avec “Anemic Domain Model” étant anti-pattern, pourquoi y a-t-il autant de systèmes qui implémentent cela?

Je pense qu’il y a plusieurs raisons

1. Complexité du système

Dans un système simple (qui correspond presque à tous les exemples et exemples de code que vous trouvez sur Internet), si je souhaite implémenter:

Ajout de produit à la commande

Je mets cette fonction sur la commande

public void Order.AddOrderLine(Product product) { OrderLines.Add(new OrderLine(product)); } 

Nice et super object orienté.

Maintenant, disons que j’ai besoin de vérifier que le produit existe dans l’inventaire et jette des exceptions si ce n’est pas le cas.

Je ne peux plus vraiment le mettre dans l’ordre, car je ne veux pas que ma commande dépende de l’inventaire, alors maintenant il doit être mis en service

 public void OrderService.AddOrderLine(Order order, Product product) { if (!InventoryService.Has(product) throw new AddProductException order.AddOrderLine(product); } 

Je pourrais également passer IInventoryService à Order.AddOrderLine, qui est une autre option, mais qui rend la commande dépendante d’InventoryService.

Order.AddOrderLine possède encore certaines fonctionnalités, mais elles sont généralement limitées à la scope de la commande, tandis que, selon mon expérience, la scope de la logique métier est bien plus importante.

Lorsque le système est plus que simple, vous vous retrouverez avec une grande partie de votre logique dans OrderService et très peu dans Order.

2. Vue du développeur sur la POO

Il y a beaucoup de discussions passionnées sur internet à propos de la logique qui devrait être appliquée aux entités.

Quelque chose comme

Ordre.Enregistrer

L’ordre devrait-il savoir se sauver ou non? Disons que nous avons des repositorys pour cela.

Maintenant, pouvez-vous append des lignes de commande? Si j’essaie d’y donner un sens en utilisant un anglais simple, cela n’a pas vraiment de sens non plus. L’utilisateur ajoute Product to Order, devrions-nous faire User.AddOrderLineToOrder ()? Cela semble exagéré.

Que diriez-vous de OrderService.AddOrderLine (). Maintenant c’est un peu logique!

Ma compréhension de la POO est que pour l’encapsulation, vous mettez des fonctions sur des classes où la fonction devra accéder à l’état interne de la classe. Si je dois accéder à la collection Order.OrderLines, je mets Order.AddOrderLine () sur Order. De cette façon, l’état interne de la classe n’est pas exposé.

3. Conteneurs IoC

Les systèmes utilisant des conteneurs IoC sont généralement totalement anémiques.

C’est parce que vous pouvez tester vos services / référentiels dotés d’interfaces, mais que vous ne pouvez pas tester facilement les objects du domaine, à moins que vous ne leur imposiez des interfaces.

Puisque “IoC” est actuellement considéré comme une solution pour tous vos problèmes de programmation, beaucoup de personnes le suivent aveuglément et se retrouvent ainsi avec les modèles de domaine anémiques.

4. La POO est difficile, la procédure est facile

J’ai un peu une ” malédiction de la connaissance ” sur celui-ci, mais j’ai découvert que pour les nouveaux développeurs ayant des DTO et des services est beaucoup plus facile que Rich Domain.

C’est probablement parce qu’avec Rich Domain, il est plus difficile de savoir sur quelles classes placer la logique. Quand créer de nouvelles classes? Quels modèles utiliser? etc..

Avec les services sans état, il vous suffit de le placer dans le service avec le nom le plus proche.

Suite à cela, il y a une pensée dans ma tête depuis très longtemps maintenant. Je pense que le terme “POO” a pris un sens qui ne lui est pas vraiment destiné. L’anagramme signifie “programmation orientée object”, comme nous le soaps tous. L’accent est mis sur le mot “orienté”. Ce n’est pas “OMP”, qui signifie “programmation mandatée par un object”. ADM et RDM sont des exemples de POO. Ils utilisent des objects, des propriétés, des interfaces de méthodes, etc. Cependant, il y a une différence entre ADM et RDM dans la façon dont nous choisissons d’encapsuler des choses. Ce sont deux choses différentes. Dire qu’ADM est mauvais OOP n’est pas une affirmation exacte. Peut-être avons-nous besoin de termes différents pour les différents niveaux d’encapsulation. De plus, je n’ai jamais aimé le terme anti-pattern. Il est généralement atsortingbué à quelque chose par les membres d’un groupe adverse. ADM et RDM sont des modèles valides, ils ont simplement des objectives différents en tête et sont destinés à répondre à différents besoins métier. Ceux d’entre nous qui pratiquent DDD devraient, à tout le moins, l’apprécier et ne pas tomber au niveau des autres en dénigrant ceux qui choisissent de mettre en œuvre ADM. Juste mes pensées.

“C’est un anti-modèle, donc les autres développeurs vous demanderont si vous comprenez les concepts de conception orientée object.”

“Un modèle de domaine anémique est un anti-pattern. Les anti-patterns n’ont pas de pros.”

Que le modèle de domaine anémique soit un anti-modèle est une question d’opinion. Martin Fowler dit que tel est le cas, un certain nombre de développeurs qui connaissent OO à l’intérieur disent que ce n’est pas le cas. Déclarer l’opinion comme un fait est rarement utile.

Un, même s’il était universellement accepté comme étant un anti-modèle, il y a des chances qu’il ait encore (mais relativement peu) un avantage.

Il me semble que l’objection principale de Fowler est que les ADM ne sont pas OO, dans le sens suivant. Si l’on conçoit un système «à partir de rien» autour de structures de données passives manipulées par d’autres éléments de code, cela ressemble plus à une conception procédurale qu’à une conception orientée object.

Je suggère qu’il existe au moins deux forces capables de produire ce type de conception:

  1. Les concepteurs / programmeurs qui pensent encore à la procédure doivent travailler dans un environnement orienté object (ou en supposant qu’ils peuvent …) produire un nouveau système, et

  2. Les développeurs travaillent à mettre un “visage” de type service sur un système hérité conçu de manière non-OO (indépendamment de la langue).

Si, par exemple, on construisait un ensemble de services pour exposer les fonctionnalités d’une application mainframe COBOL existante, on pourrait définir des services et des interfaces en termes de modèle conceptuel ne reflétant pas les structures de données COBOL internes. Cependant, si le service mappe le nouveau modèle aux anciennes données pour utiliser l’implémentation existante mais cachée, le nouveau modèle pourrait très bien être “anémique” au sens de l’article de Fowler – par exemple, un ensemble de définitions de style TransferObject. et des relations sans comportement réel.

Ce type de compromis peut très bien être courant pour les limites auxquelles les systèmes OO idéalement purs doivent interagir avec un environnement non-OO existant.

Le modèle de domaine anémique (ADM) peut être un bon choix si votre équipe est incapable ou refuse de créer un modèle de domaine riche (RDM) et de le maintenir au fil du temps. Gagner avec un RDM nécessite une attention particulière aux abstractions dominantes utilisées dans le système. Figure que, dans tout groupe de développement, pas plus de la moitié et peut-être seulement un dixième de ses membres sont compétents avec des abstractions. À moins que ce cadre (peut-être un seul développeur) soit en mesure de maintenir son influence sur l’ensemble des activités du groupe, le RDM succombera à l’entropie.

Et le RDM entropique fait mal, de manière particulière. Ses développeurs apprendront des leçons difficiles. Au début, ils seront en mesure de répondre aux attentes de leurs parties prenantes, car ils n’auront aucun historique à respecter. Mais comme leur système devient plus compliqué (pas complexe), il deviendra fragile. les développeurs essaieront de réutiliser le code mais auront tendance à induire de nouveaux bogues ou à effectuer des retours en arrière dans le développement (et donc à dépasser leurs estimations).

En revanche, les développeurs ADM se fixent des attentes plus faibles, car ils ne s’attendent pas à réutiliser autant de code pour les nouvelles fonctionnalités. Au fil du temps, ils auront un système avec de nombreuses incohérences, mais cela ne se répètera probablement pas de manière inexacte. Leur temps de mise sur le marché sera plus long qu’avec un RDM réussi, mais il est peu probable que leurs parties prenantes perçoivent cette possibilité.

“Les développeurs travaillent à mettre un” visage “de type service sur un système hérité conçu de manière non-OO (indépendamment de la langue).”

Si vous pensez à de nombreuses applications métier, ces systèmes hérités n’utiliseront souvent pas le même modèle de domaine que vous. Le modèle de domaine anémique résout ce problème en utilisant la logique métier dans les classes de service. Vous pouvez mettre tout ce code d’interface dans votre modèle (au sens traditionnel des OO) – mais vous perdez généralement de la modularité.

Quand je suis tombé sur l’article sur le modèle de domaine anémique, je me suis dit «sainte s ***, c’est ce que je fais. J’ai persévéré et suivi les références au livre d’Eric Evan, considéré comme un bon exemple, et téléchargé la source. Il s’avère que «ne pas utiliser un modèle de domaine anémique» ne signifie pas «ne pas utiliser de classes de service, ne pas utiliser de médiateurs, ne pas utiliser de stratégies» ou même «mettre la logique sur la classe manipulée».

Les exemples DDD ont des classes de service, XyzUpdaters, singletons et IoC.

Je rest confus par ce qu’est exactement un modèle de domaine anémique. Je m’attends à “je le saurai quand je le verrai”. Pour l’instant, je suis satisfait d’un exemple positif de bonne conception.

C’est le même que pour la plupart des anti-patterns: cela permet de garder beaucoup de monde occupé pendant longtemps. Comme les gestionnaires ont tendance à être plus payés lorsqu’ils gèrent plus de personnes, il y a une forte incitation à ne pas s’améliorer.

Conformément à la réponse d’Eric P et à ce que d’autres ont écrit, il semble que le principal inconvénient d’un ADM soit la perte de OOD, en particulier le maintien de la logique et des données d’un concept de domaine afin de masquer les détails de l’implémentation. l’API peut être riche.

Eric continue en signalant qu’il y a souvent des informations en dehors d’une classe de domaine qui sont nécessaires pour la logique d’agir sur cette classe, comme vérifier un inventaire avant d’append un article à une commande. Je me demande toutefois si la réponse est une couche de service contenant cette logique globale ou si elle est mieux gérée dans le cadre de la conception de l’object. Quelqu’un doit connaître l’object Inventory, l’object Product et l’object Order. Peut-être est-ce simplement un object OrderSystem, qui a un membre Inventory, une liste de commandes, etc. Cela ne sera pas très différent d’un service, mais je pense qu’il est conceptuellement plus cohérent.

Ou regardez comme ceci: Vous pouvez avoir un utilisateur avec un solde de crédit interne, et chaque fois que User.addItemToOrder (item) est appelé, il obtient le prix de l’article et vérifie le crédit avant de l’append, etc. conception. Je ne suis pas sûr de ce qui est perdu en remplaçant cela par Service.addItemToUserOrder (utilisateur, item), mais je ne suis pas sûr non plus de ce qui a été gagné. Je suppose qu’une perte serait la couche supplémentaire de code, plus le style d’écriture plus compliqué et l’ignorance forcée du modèle de domaine sous-jacent.

Ayant travaillé avec un système «mature» avec un ADM et estimant pouvoir fournir au moins un retour anecdotique à cette question.

1) Manque d’encapsulation

Dans un système live avec un ADM, il y a la possibilité d’écrire par exemple ‘obj.x = 100; obj.save ‘, même si cela viole la logique métier. Cela conduit à un certain nombre de bogues qui ne seraient pas rencontrés si les invariants étaient modélisés sur l’object. Je pense que c’est la sévérité et l’omniprésence de ces insectes qui sont les plus graves pour un SMA.

Je pense qu’il est important de souligner ici que c’est là qu’une solution fonctionnelle et les solutions procédurales d’un ADM diffèrent de manière significative et que les similarités que d’autres peuvent avoir trouvées avec les similarités de surface entre un ADM et une solution fonctionnelle sont accessoires.

2) Ballonnement du code

J’estime que la quantité de code produite dans un ADM est de 5 à 10 fois celle que créerait une solution OOP / RDM. Cela s’explique peut-être par la répétition du code à 50%, le code de la plaque chauffante pour 30% et la résolution ou la résolution de problèmes dus au manque de RDM.

3) Mauvaise compréhension des problèmes du domaine

Un ADM et une mauvaise compréhension des problèmes du domaine vont de pair. Des solutions naïves se posent, les exigences sont mal sockets en compte en raison de la difficulté à les prendre en charge avec le DM existant, et l’ADM devient un obstacle important à l’innovation commerciale compte tenu des délais de développement plus longs et du manque de flexibilité.

4) Difficulté d’entretien

Un certain niveau de rigueur est nécessaire pour garantir qu’un concept de domaine est modifié à tous les endroits où il est exprimé, étant donné que le concept peut ne pas être simplement une réimplémentation par copier-coller. Cela conduit souvent à rechercher et à corriger les mêmes bugs à plusieurs resockets.

5) Difficulté accrue avec l’intégration

Je pense que l’un des avantages d’un RDM est la cohésion des concepts qui permettent une compréhension plus rapide du domaine. Avec un ADM, les concepts peuvent être fragmentés et manquer de clarté, ce qui est plus difficile à acquérir pour les nouveaux développeurs.

J’étais également tenté d’inclure les coûts de soutien opérationnel pour un SMA plus élevé que pour un RDM, mais cela dépendrait d’un certain nombre de facteurs.

Comme d’autres l’ont fait remarquer, jetez un coup d’œil à DDD (Greg Evans, Vince Vaughn et Scott Millett) pour les avantages d’un RDM.

Pour étendre la réponse de Michael, j’aurais pensé qu’il était (assez) clair où devrait aller ce code: dans un médiateur dédié qui gère l’interaction entre la commande et l’inventaire.

De mon sharepoint vue, l’essentiel à propos du domaine est qu’il DOIT contenir le comportement de test simple des méthodes isInThisState() , etc. Selon mon expérience, elles sont également dispersées dans les larmes de service (sic :)) dans la plupart réécrit. Tout cela enfreint les règles de cohésion standard.

À mon avis, l’approche devrait consister à viser un DM qui possède autant de comportement professionnel que possible, le rest dans des domaines clairement désignés (c’est-à-dire pas dans les services).

Mon équipe préfère personnellement l’ADM. Nous avons un ensemble d’objects métier qui représentent des parties spécifiques de notre domaine. Nous utilisons des services pour enregistrer ces objects dans la firebase database. Nos objects métier ont des méthodes, mais ces méthodes ne manipulent que son état interne.

L’avantage pour nous d’utiliser ADM sur RDM peut être vu dans la manière dont nous persistons à db. Les développeurs travaillant sur nos systèmes de code hérités peuvent utiliser nos objects métier (à partir du nouveau système) et continuer à utiliser leur couche d’access aux données actuelle pour conserver ces objects dans la firebase database. L’utilisation du RDM forcerait les développeurs de notre ancien système à injecter des objects du référentiel dans notre modèle de gestion … ce qui ne serait pas cohérent avec leur couche d’access aux données actuelle.

Il convient de noter que, à mesure que les systèmes se développent en complexité et en granularité, l’encapsulation et la consolidation des points d’interface offerts par un modèle d’object

Les couches de service créées par l’ADM, certes plus faciles à implémenter (car elles nécessitent peu de reflection et de nombreux points d’interface décentralisés) créeront probablement des problèmes plus tard, lorsqu’il est temps de modifier un système actif.

J’appendais que tous les cas ne nécessitent pas un modèle de domaine (et encore moins un modèle ADM). Parfois, il est préférable d’utiliser un style plus procédural / fonctionnel de la tâche, qui dépend des données et ne dépend pas des règles logiques / métier à l’échelle de l’application.

Si vous essayez de décider des avantages et des inconvénients pour une application entière, je pense qu’il est important de concevoir d’abord ce que chaque application pourrait ressembler AVANT de commencer à écrire une seule ligne de code. Une fois que vous avez mis en forme votre application CRC dans les deux styles, prenez du recul et décidez lequel est le plus approprié et qui correspond mieux à l’application.

Pensez aussi à celui qui sera plus facile à maintenir …

Cela donne une meilleure prévisibilité. Les gestionnaires aiment ça, surtout si le projet est rémunéré en temps et en matériel. Chaque changement implique beaucoup de travail, alors un travail difficile peut être caché derrière beaucoup de travail répétitif. Dans un système DRY bien conçu, la prévisibilité est très mauvaise, car vous faites constamment de nouvelles choses.

Un modèle de domaine anémique est un anti-pattern. Les anti-patrons n’ont pas de pros.