Est-ce que «l'inversion du contrôle» favorise le «modèle de domaine anémique»?

32

Lorsque j'ai utilisé IoC Container dans mon dernier projet, je me suis retrouvé avec des entités anémiques et la plupart de ma logique métier dans Stateless Services.

J'ai vu des projets écrits par d'autres développeurs qui utilisent "Inversion of Control" et ils sont toujours "Anemic".

Étant donné que le «modèle de domaine anémique» est anti-modèle, est-il possible d'utiliser l'IoC et le domaine riche? Y a-t-il de bons exemples, des projets open source qui font ça?

Mag20
la source
Je pense que nous aurions besoin de voir des exemples spécifiques de votre cas particulier pour vous aider.
Martijn Verburg,
1
Désolé, je voulais dire des extraits de code :)
Martijn Verburg

Réponses:

11

Pour commencer: DI et IoC ne sont pas synonymes. Je suis désolé mais je dois le signaler (il me semble que vous pensez qu'ils le sont).

Quant à votre demande ... Eh bien, l'injection de dépendance n'est qu'un outil. Comment vous allez utiliser cet outil est une chose complètement distincte. Il existe également d'autres outils (modèles de conception) qui pourraient s'ajouter au problème. Par exemple, je pense qu'une large adoption du modèle MVC est l'un des ingrédients clés de la formation de l'anti-modèle du modèle de domaine anémique: les contrôleurs (dans les applications plus simples, dans les plus complexes qui seraient une couche de service supplémentaire) assument la responsabilité de la validation des règles métier. , en les appliquant ainsi qu'en transformant les entités de base de données en quelque chose d'utile, tandis que Business Layer se transforme en simple couche d'accès aux données qui est un ORM simple avec un mappage un à un vers les entités de base de données.

C'est certainement la façon dont vous concevez votre application - vous pouvez créer un modèle de domaine correct si vous le souhaitez, et tous ces IoC, DI, MVC ne vous arrêtent pas. Ce qui pourrait vous arrêter, c'est votre équipe. Vous devez en quelque sorte les convaincre d'utiliser le bon chemin et cela peut être difficile car de nombreux développeurs de logiciels n'ont pas une solide expérience architecturale.

Paweł Dyda
la source
J'ajouterai à cela que vous pourriez peut-être jeter un œil à l'approche DDD adoptée par Eric Evans et al.
Martijn Verburg,
1
J'ai lu le livre d'Eric Evans. Il est bon pour la méthodologie générale et le langage omniprésent, mais manque quelque peu d'exemples concrets.
Mag20
Merci d'avoir souligné la différence entre DI et IoC. Je pense que le problème était davantage lié à l'IoC qu'à la DI. Modification de la question pour refléter cela.
Mag20
D'après mon expérience avec les frameworks / conteneurs DI (Spring DI, CDI, Unity), ils vous empêchent en effet de créer un "modèle de domaine correct", ce qui signifie pour moi que les développeurs ne devraient pas être contraints d'utiliser des objets vrais (c'est-à-dire avec état) . Mais DI ne supporte pas vraiment cela.
Rogério
8

La plupart (sinon la totalité) des applications sont un mélange de problèmes d'infrastructure et de domaine. Lorsque vous atteignez un certain niveau de complexité, vous faciliterez la gestion si le domaine est séparé de l'infrastructure afin qu'il soit plus facile de raisonner et d'évoluer indépendamment.

Bien sûr, le modèle de domaine doit toujours communiquer avec le reste du système et ce sera généralement avec des services sans état (qui font partie du domaine) qui ont des problèmes d'infrastructure (tels que l'accès à la base de données) qui leur sont injectés. L'utilisation d'un conteneur IoC ne supprime pas cette dépendance, il déplace sa configuration dans une zone distincte - ce qui facilite encore plus le raisonnement et la maintenance.

Les entités stockent l'état et devraient être responsables des règles commerciales. Si vos services appliquent tous les invariants et autres règles métier, il est probable que la logique est au mauvais endroit.

Maintenant, si vous avez la logique aux bons endroits et que vous vous êtes quand même retrouvé avec des services qui ne sont que des enveloppes autour d'infrastructures et d'entités qui ne sont que des sacs de propriété, il est très probable que le domaine ne soit pas assez complexe pour justifier les frais généraux de son propre modèle. À peu près tout ce que vous lirez sur DDD contiendra une clause de non-responsabilité qui n'est vraiment destinée qu'aux domaines complexes, mais cela semble trop souvent oublié.

FinnNk
la source
7

Allez à la source. Commencez avec l'article de Fowler sur les modèles de domaine anémique . Il fait référence à la conception pilotée par le domaine d'Eric Evan comme exemple de bonne pratique. Le code source pour cela est ici . Télécharge le.

Notez qu'il utilise Inversion of Control (recherchez @Autowired), et a des classes de service (BookingService) et des classes "processus métier" (par exemple ItineraryUpdater).

L'article original de Fowler ouvre la voie à l'exemple que vous recherchez.

Jamie
la source
Cet exemple d'application, en fait, n'est pas conforme à DDD comme décrit dans le livre. Une contradiction spécifique avec le livre est qu'il viole complètement le concept d '"infrastructure", en lui permettant de contenir du code spécifique au domaine; par exemple, la VoyageRepositoryHibernateclasse, qui a été placée dans la couche infrastructure mais dépend en fait de la couche domaine.
Rogério
Oui, le livre dit à la page 73 que la couche infrastructure est "en dessous" de la couche domaine et "elle ne devrait pas avoir de connaissance particulière du domaine qu'elle dessert". Cela n'a jamais eu de sens pour moi. Considérons un projet qui a deux implémentations VoyageRepository: VoyageRepositoryHibernate et une classe VoyageRepositoryJDBC. Leurs implémentations sont nécessairement très différentes et spécifiques à la technologie. Font-ils partie de la couche domaine? Ou la couche infrastructure? Dans notre code, selon le livre, nous le faisons à l'envers: la couche infrastructure peut référencer la couche domaine, mais pas l'inverse.
jamie
Ils appartiennent à la couche domaine, oui. L'implémentation basée sur JDBC contiendrait du code SQL lié aux tables et colonnes de la base de données d'application, qui sont spécifiques au domaine. Mettre n'importe quel code spécifique à un domaine ou à une application dans la couche d'infrastructure est tout simplement faux, car le "code d'infrastructure" ne devrait être utilisé que pour résoudre des problèmes techniques et devrait (idéalement) être entièrement réutilisable entre différentes applications et différents domaines. La solution pour avoir du code de "bas niveau" (par exemple SQL) dans la couche de domaine n'est pas de le déplacer complètement, mais de l'implémenter au-dessus d'une meilleure infrastructure, comme ORM.
Rogério
Pour moi, l'implémentation de save (MyDomainObject foo) est une préoccupation purement technique. YMMV.
jamie
Seulement si cela ne vous amène pas à violer la règle fondamentale d'une architecture en couches: une couche inférieure ne peut dépendre d'une couche supérieure. Ainsi, si vous avez implémenté save(foo)un code susceptible de changer lorsque le modèle de domaine change (par exemple, si un nouvel attribut est ajouté à MyDomainObject), il doit (par définition) appartenir à la couche domaine; sinon, vous ne pouvez plus parler d'avoir des "couches".
Rogério
7

est-il possible d'utiliser IoC et Rich Domain? Y a-t-il de bons exemples, des projets open source qui font ça?

Je suppose que vous voulez dire DI au lieu d'IoC, et le projet sur lequel vous avez travaillé utilise un conteneur DI comme Spring. IoC a deux saveurs principales: DI et modèle de localisateur. Je ne vois pas pourquoi le modèle de localisateur devrait être un problème, alors concentrons-nous sur DI.

Je ne pense pas que ce soit possible, ou du moins ce serait très impraticable. L'aspect principal des conteneurs DI est qu'ils contrôlent la création d'objets lorsqu'ils les injectent dans d'autres ("objets gérés"). L'ensemble d'objets gérés qui est vivant lorsque les projets s'exécutent est indépendant des éléments de domaine qui existent dans votre projet, mais dépend de la façon dont les objets sont câblés et des étendues (singleton, prototype) qui leur sont affectées.

C'est pourquoi vous ne voulez pas laisser le conteneur DI gérer vos objets de domaine. Mais si vous créez des objets manuellement (avec de nouveaux), vous ne pouvez pas obtenir d'autres objets injectés dans vos objets de domaine. (Laissant de côté les solutions de contournement potentielles avec un câblage manuel.) Étant donné que vous avez besoin de ces injections pour remplacer les implémentations par d'autres, vous ne pouvez pas remplacer la fonctionnalité des objets de domaine riche à l'aide de DI. Par conséquent, vous ne voudrez pas placer de fonctionnalité dans les objets de domaine, ou vous perdriez les fonctionnalités de la DI.

Je ne vois pas comment un conteneur DI hypothétique pourrait fonctionner sans gérer vos objets, et aucune des implémentations existantes ne le permet. Il est donc juste de prétendre que DI repose sur la gestion des objets. Il vous tentera donc toujours de diviser les objets Rich Domain potentiels en une classe anémique et une ou plusieurs classes de script de transaction.

Wolfgang
la source
Cette réponse frappe vraiment le clou sur la tête en ce qui concerne la tension entre un modèle de domaine riche et et l'injection de dépendance.
jrahhali