Je décide si je devrais utiliser un modèle de domaine riche sur un modèle de domaine anémique et je recherche de bons exemples des deux.
J'ai créé des applications Web à l'aide d'un modèle de domaine anémique, soutenu par un système de couche Service -> Repository -> Storage , en utilisant FluentValidation pour la validation BL et en plaçant tout mon BL dans la couche Service.
J'ai lu le livre DDD d'Eric Evan, et lui (avec Fowler et d'autres) semble penser que les modèles de domaine anémique sont un anti-pattern.
Je voulais donc vraiment avoir un aperçu de ce problème.
En outre, je suis vraiment à la recherche de bons exemples (de base) d'un modèle de domaine riche et des avantages par rapport au modèle de domaine anémique qu'il offre.
Réponses:
La différence est qu'un modèle anémique sépare la logique des données. La logique est souvent placé dans les classes nommées
**Service
,**Util
,**Manager
,**Helper
et ainsi de suite. Ces classes implémentent la logique d'interprétation des données et prennent donc le modèle de données comme argument. Par exempletandis que l'approche du domaine riche inverse cela en plaçant la logique d'interprétation des données dans le modèle du domaine riche. Ainsi, il associe la logique et les données et un modèle de domaine riche ressemblerait à ceci:
Cela a un impact important sur la cohérence des objets. Puisque la logique d'interprétation des données encapsule les données (les données ne sont accessibles que via des méthodes objet), les méthodes peuvent réagir aux changements d'état d'autres données -> C'est ce que nous appelons le comportement.
Dans un modèle anémique, les modèles de données ne peuvent pas garantir qu'ils sont dans un état légal alors qu'ils le peuvent dans un modèle de domaine riche. Un modèle de domaine riche applique des principes OO tels que l'encapsulation, le masquage d'informations et le rapprochement des données et de la logique.Par conséquent, un modèle anémique est un anti-pattern d'un point de vue OO.
Pour un aperçu plus approfondi, jetez un œil à mon blog https://www.link-intersystems.com/blog/2011/10/01/anemic-vs-rich-domain-models/
la source
Bozhidar Bozhanov semble plaider en faveur du modèle anémique dans ce billet de blog.
Voici le résumé qu'il présente:
les objets de domaine ne doivent pas être gérés par le ressort (IoC), ils ne doivent pas avoir de DAO ou quoi que ce soit lié à l'infrastructure injecté en eux
les objets de domaine ont les objets de domaine dont ils dépendent définis par la mise en veille prolongée (ou le mécanisme de persistance)
les objets de domaine exécutent la logique métier, comme l'idée de base de DDD est, mais cela n'inclut pas les requêtes de base de données ou CRUD - uniquement les opérations sur l'état interne de l'objet
il y a rarement besoin de DTO - les objets de domaine sont les DTO eux-mêmes dans la plupart des cas (ce qui permet d'économiser du code standard)
les services effectuent des opérations CRUD, envoient des e-mails, coordonnent les objets de domaine, génèrent des rapports basés sur plusieurs objets de domaine, exécutent des requêtes, etc.
la couche de service (application) n'est pas si fine, mais n'inclut pas les règles métier intrinsèques aux objets du domaine
la génération de code doit être évitée. L'abstraction, les modèles de conception et la DI devraient être utilisés pour surmonter le besoin de génération de code et, finalement, pour se débarrasser de la duplication de code.
METTRE À JOUR
J'ai récemment lu cet article où l'auteur préconise de suivre une sorte d'approche hybride - les objets du domaine peuvent répondre à diverses questions en fonction uniquement de leur état (ce qui dans le cas de modèles totalement anémiques serait probablement fait dans la couche service)
la source
Mon point de vue est le suivant:
Modèle de domaine anémique = tables de base de données mappées sur des objets (uniquement des valeurs de champ, pas de comportement réel)
Modèle de domaine riche = une collection d'objets qui exposent le comportement
Si vous souhaitez créer une application CRUD simple, peut-être qu'un modèle anémique avec un framework MVC classique suffit. Mais si vous souhaitez implémenter une sorte de logique, le modèle anémique signifie que vous ne ferez pas de programmation orientée objet.
* Notez que le comportement des objets n'a rien à voir avec la persistance. Une couche différente (mappeurs de données, référentiels, etc.) est responsable de la persistance des objets de domaine.
la source
x
,y
,sum
etdifference
. Ça fait quatre choses. Ou vous pourriez argumenter que c'est l'addition et la soustraction (deux choses). Ou vous pourriez dire que ce sont les mathématiques (une chose). Il existe de nombreux articles de blog sur la façon de trouver un équilibre dans l'application du SRP. En voici un: hackernoon.comLa figure 1 montre un modèle de domaine anémique, qui est essentiellement un schéma avec des getters et des setters.
Dans ce modèle plus riche, plutôt que d'exposer simplement les propriétés à lire et à écrire, la surface publique de Customer est constituée de méthodes explicites.
la source
Address
, maisExtendedAddress
hérité deAddress
, avec plusieurs propriétés supplémentaires? 2) Ou changerCustomerCreditCard
les paramètres du constructeur à prendreBankID
au lieu deBankName
?L'un des avantages des classes de domaine riches est que vous pouvez appeler leur comportement (méthodes) chaque fois que vous avez la référence à l'objet dans n'importe quelle couche. De plus, vous avez tendance à écrire des méthodes petites et distribuées qui collaborent ensemble. Dans les classes de domaine anémique, vous avez tendance à écrire des méthodes procédurales lourdes (dans la couche de service) qui sont généralement gérées par un cas d'utilisation. Ils sont généralement moins maintenables que les classes de domaine riches.
Un exemple de classes de domaine avec des comportements:
La méthode
needToDeliver()
retournera la liste des articles qui doivent être livrés, y compris les bonus. Il peut être appelé à l'intérieur de la classe, depuis une autre classe associée ou depuis une autre couche. Par exemple, si vous passezOrder
à la vue, vous pouvez utiliser les élémentsneedToDeliver()
sélectionnésOrder
pour afficher la liste des éléments à confirmer par l'utilisateur avant de cliquer sur le bouton Enregistrer pour conserver leOrder
.Répondre au commentaire
Voici comment j'utilise la classe de domaine du contrôleur:
La création de
Order
et sonLineItem
est en une seule transaction. Si l'un desLineItem
ne peut pas être créé, nonOrder
ne sera créé.J'ai tendance à avoir une méthode qui représente une seule transaction, telle que:
Tout ce qu'il y a à l'intérieur
deliver()
sera exécuté en une seule transaction. Si je dois exécuter de nombreuses méthodes non liées en une seule transaction, je créerais une classe de service.Pour éviter une exception de chargement paresseux, j'utilise le graphique d'entité nommé JPA 2.1. Par exemple, dans l'écran du contrôleur pour la livraison, je peux créer une méthode pour charger l'
delivery
attribut et ignorerbonus
, commerepository.findOrderByNumberFetchDelivery()
. Dans l'écran bonus, j'appelle une autre méthode qui charge l'bonus
attribut et l'ignoredelivery
, telle querepository.findOrderByNumberFetchBonus()
. Cela nécessite dicipline car je ne peux toujours pas appeler à l'deliver()
intérieur de l'écran bonus.la source
Lorsque j'écrivais des applications de bureau monolithiques, je construisais des modèles de domaine riches, j'avais l'habitude de les créer.
Maintenant, j'écris de minuscules microservices HTTP, il y a aussi peu de code que possible, y compris les DTO anémiques.
Je pense que DDD et cet argument anémique datent de l'ère monolithique des applications de bureau ou de serveur. Je me souviens de cette époque et je conviens que les modèles anémiques sont étranges. J'ai construit une grande application de trading FX monolithique et il n'y avait pas de modèle, vraiment, c'était horrible.
Avec les microservices, les petits services avec leur comportement riche sont sans doute les modèles composables et les agrégats au sein d'un domaine. Ainsi, les implémentations de microservices elles-mêmes peuvent ne pas nécessiter de DDD supplémentaire. L'application de microservice peut être le domaine.
Un microservice de commandes peut avoir très peu de fonctions, exprimées en ressources RESTful ou via SOAP ou autre. Le code du microservice des commandes peut être extrêmement simple.
Un service unique (micro) monolithique plus grand, en particulier celui qui le maintient en RAM, peut bénéficier de DDD.
la source
Je pense que la racine du problème réside dans une fausse dichotomie. Comment extraire ces 2 modèles: riche et "anémique" et les opposer? Je pense que ce n'est possible que si vous avez une mauvaise idée de ce qu'est une classe . Je ne suis pas sûr, mais je pense que je l'ai trouvé dans l'une des vidéos de Bozhidar Bozhanov sur Youtube. Une classe n'est pas une data + des méthodes sur ces données. C'est une compréhension totalement invalide qui conduit à la division des classes en deux catégories: données uniquement, donc modèle anémique et données + méthodes - modèle si riche (pour être plus correct il y a une 3ème catégorie: les méthodes seulement même).
Le vrai est que la classe est un concept dans un modèle ontologique, un mot, une définition, un terme, une idée, c'est un DENOTAT . Et cette compréhension élimine les fausses dichotomies: vous ne pouvez pas avoir SEULEMENT un modèle anémique ou UNIQUEMENT un modèle riche, car cela signifie que votre modèle n'est pas adéquat, ce n'est pas pertinent par rapport à la réalité: certains concepts n'ont que des données, certains d'entre eux n'ont que des méthodes, d'autres d’entre eux sont mixtes. Parce que nous essayons de décrire, dans ce cas, certaines catégories, ensembles d'objets, relations, concepts avec des classes, et comme nous le savons, certains concepts sont des processus uniquement (méthodes), certains d'entre eux sont des ensembles d'attributs uniquement (données), certains ce sont des relations avec des attributs (mixtes).
Je pense qu'une application adéquate devrait inclure toutes sortes de classes et éviter de s'auto-limiter fanatiquement à un seul modèle. Peu importe, comment la logique représente: avec du code ou avec des objets de données interprétables (comme les Monades libres ), de toute façon: nous devrions avoir des classes (concepts, dénotats) représentant des processus, de la logique, des relations, des attributs, des caractéristiques, des données, etc. et non d'essayer d'en éviter certains ou de les réduire tous à un seul type.
Ainsi, nous pouvons extraire la logique d'une autre classe et laisser les données dans la classe d'origine, mais cela n'a pas de sens car certains concepts peuvent inclure des attributs et des relations / processus / méthodes et une séparation de ceux-ci dupliquera le concept sous 2 noms qui peuvent être réduit aux motifs: "OBJECT-Attributes" et "OBJECT-Logic". C'est bien dans les langages procéduraux et fonctionnels en raison de leur limitation, mais c'est une trop grande retenue pour un langage qui vous permet de décrire toutes sortes de concepts.
la source
Les modèles de domaine anémique sont importants pour l'ORM et le transfert facile sur les réseaux (le sang vital de toutes les applications commerciales) mais OO est très important pour l'encapsulation et la simplification des parties «transactionnelles / manipulation» de votre code.
Par conséquent, ce qui est important, c'est de pouvoir identifier et convertir d'un monde à l'autre.
Nommez les modèles Anemic quelque chose comme AnemicUser, ou UserDAO etc. pour que les développeurs sachent qu'il y a une meilleure classe à utiliser, puis ont un constructeur approprié pour la classe none Anemic
et méthode d'adaptateur pour créer la classe anémique pour le transport / la persistance
Visez à utiliser l'utilisateur non anémique partout en dehors du transport / de la persistance
la source
Voici un exemple qui pourrait vous aider:
Anémique
Non anémique
la source
L'approche classique de DDD n'indique pas d'éviter à tout prix les modèles anémiques vs riches. Cependant, MDA peut toujours appliquer tous les concepts DDD (contextes bornés, cartes de contexte, objets de valeur, etc.) mais utiliser les modèles Anemic vs Rich dans tous les cas. Il existe de nombreux cas où l'utilisation des services de domaine pour orchestrer des cas d'utilisation de domaine complexes sur un ensemble d'agrégats de domaine constitue une bien meilleure approche que de simples agrégats appelés à partir de la couche d'application. La seule différence par rapport à l'approche DDD classique est où se trouvent toutes les validations et règles métier? Il existe une nouvelle construction connue sous le nom de validateurs de modèle. Les validateurs garantissent l'intégrité du modèle d'entrée complet avant tout cas d'utilisation ou flux de travail de domaine. Les entités agrégées racine et enfants sont anémiques mais chacune peut avoir ses propres validateurs de modèle invoqués si nécessaire, par son validateur racine. Les validateurs adhèrent toujours au SRP, sont faciles à entretenir et peuvent être testés à l'unité.
La raison de ce changement est que nous nous dirigeons maintenant davantage vers une première approche API plutôt qu'une première approche UX des microservices. REST a joué un rôle très important à cet égard. L'approche API traditionnelle (à cause de SOAP) était initialement fixée sur une API basée sur des commandes par rapport aux verbes HTTP (POST, PUT, PATCH, GET et DELETE). Une API basée sur des commandes correspond bien à l'approche orientée objet Rich Model et est toujours très valable. Cependant, les API simples basées sur CRUD, bien qu'elles puissent s'intégrer dans un modèle riche, sont bien mieux adaptées avec des modèles anémiques simples, des validateurs et des services de domaine pour orchestrer le reste.
J'adore DDD dans tout ce qu'il a à offrir, mais il arrive un moment où vous devez l'étirer un peu pour l'adapter à une approche en constante évolution et meilleure de l'architecture.
la source