web-dev-qa-db-fra.com

Pièges de la conception pilotée par domaine avec Entity Framework

Beaucoup de tutoriels sur DDD que j'ai étudiés couvrent principalement la théorie. Ils ont tous des exemples de code rudimentaires (Pluralsight et similaires).

Sur le Web, quelques personnes tentent également de créer des didacticiels couvrant DDD avec EF. Si vous commencez à les étudier brièvement - vous remarquez rapidement qu'ils diffèrent beaucoup les uns des autres. Certaines personnes recommandent de garder l'application minimale et pour éviter d'introduire des couches supplémentaires, par exemple un référentiel au-dessus d'EF, d'autres génèrent décidément des couches supplémentaires, souvent même en violant SRP en injectant DbContext dans Racines agrégées .

Je m'excuse terriblement si je pose une question d'opinion, mais ...

En ce qui concerne la pratique - Entity Framework est l'un des ORM les plus puissants et les plus largement utilisés. Malheureusement, vous ne trouverez pas de cours complet couvrant DDD.


Aspects importants:

  • Entity Framework sort UoW & Repository (DbSet) de la boîte

  • avec EF vos modèles ont propriétés de navigation

  • avec EF tous les modèles sont toujours disponibles off DbContext (ils sont représentés comme DbSet)

Pièges:

  • vous impossible garantissez que vos modèles enfants ne sont affectés que via la racine agrégée - vos modèles ont des propriétés de navigation et il est possible de les modifier et d'appeler dbContext.SaveChanges()

  • avec DbContext vous pouvez accéder à tous vos modèles, donc contourner Racine agrégée

  • vous pouvez restreindre l'accès aux enfants de l'objet racine via ModelBuilder dans OnModelCreating méthode en les marquant comme des champs - Je ne pense toujours pas que ce soit la bonne façon de procéder DDD et il est difficile d'évaluer le type d'aventures que cela pourrait mener à l'avenir ( assez sceptique )

Conflits:

  • sans implémenter une autre couche de référentiel qui retourne Aggregate nous ne pouvons même pas résoudre en partie les pièges mentionnés ci-dessus

  • en implémentant une couche supplémentaire de référentiel, nous ignorons les fonctionnalités intégrées d'EF (chaque DbSet est déjà un dépôt) et compliquons trop l'application


Ma conclusion:

Veuillez excuser mon ignorance, mais sur la base des informations ci-dessus - c'est soit Entity Framework n'est pas adéquat pour la conception pilotée par domaine ou la conception pilotée par domaine est un imparfait et - obsolète approche.

Je soupçonne que chacune des approches a ses mérites, mais je suis complètement perdu maintenant et je n'ai pas la moindre idée de la façon de réconcilier EF avec DDD.


Si je me trompe - quelqu'un pourrait-il au moins détailler un simple ensemble d'instructions (ou même fournir des exemples de code décents) sur la façon de traiter DDD avec EF, s'il vous plaît?

11
Alex Herman

DDD et EF n'ont presque rien à voir l'un avec l'autre.

DDD est un concept de modélisation. Cela signifie penser au domaine, aux exigences commerciales et les modéliser. Surtout dans le contexte de l'orientation objet, cela signifie créer un design qui reflète les fonctions et capacités de l'entreprise.

EF est une technologie de persistance. Il concerne principalement les données et les enregistrements de bases de données.

Ces deux sont fortement divorcés. Une conception DDD peut utiliser EF sous une forme sous le capot, mais les deux ne doivent pas interagir de quelque autre manière.

Certaines interprétations de la conception pilotée par domaine préconisent en fait la modélisation des données, et je pense que c'est de cela qu'il s'agit. Dans cette interprétation, les "entités" et les "objets de valeur" sont essentiellement des détenteurs de données sans fonction uniquement, et la conception se préoccupe des propriétés qu'elles détiennent et de la relation qu'elles ont entre elles. Dans ce contexte, DDD vs. EF peuvent apparaître.

Cette interprétation est cependant imparfaite, et je recommanderais fortement de l'ignorer complètement.

En conclusion: DDD et EF ne s'excluent pas mutuellement, ils sont en fait sans rapport l'un avec l'autre, tant que vous effectuez une modélisation d'objet appropriée et non une modélisation de données. Les objets DDD ne doivent en aucun cas être des artefacts EF. Les entités DDD doivent pas être des "entités" EF par exemple. À l'intérieur d'une fonction pertinente pour l'entreprise, une conception DDD peut utiliser EF avec certains objets de données associés, mais ceux-ci doivent toujours être cachés sous une interface orientée comportementale pertinente pour l'entreprise.

8
Robert Bräutigam

Traitez EF pour ce qu'il est, c'est-à-dire une bibliothèque d'accès aux données qui n'est que légèrement plus fortement typée que ADO.NET brut. Je ne recommanderais pas de modéliser votre domaine à l'aide de classes d'entités EF, tout comme je ne recommanderais pas de modéliser un domaine à l'aide de DataSet ou DataTable bruts.

Je comprends que EF est vendu comme un raccourci entre l'accès à la base de données et la modélisation de domaine, mais cette approche est intrinsèquement défectueuse car elle résout deux problèmes largement indépendants. Il y a eu d'autres tentatives dans .NET pour faire en sorte qu'une classe exécute des choses complètement indépendantes (par exemple .NET Remoting) et elles ne se sont pas bien terminées.

Faites le DDD à l'aide des classes POCO et ne laissez pas le schéma de base de données piloter votre conception. Gardez EF à l'intérieur du référentiel/couche de persistance et ne laissez pas les entités EF fuir à l'extérieur.

6
KolA

Entity Framework sort UoW & Repository (DbSet) de la boîte

Non.

Les abstractions Entity Framework ont ​​été construites avec ORM, pas DDD, à l'esprit. L'abstraction DbSet dans n'importe quelle version d'Entity Framework est loin de la simplicité d'un référentiel DDD - sans parler de DbContext qui expose un million de choses plus qu'un UnitOfWork.

Voici une liste non exhaustive des éléments du résumé d'EF Core 2.1 DbSet<TEntity> Dont nous n'avons pas besoin dans DDD:

  • Attach(TEntity) et tous ses frères et sœurs
  • Find(Object[])
  • Update(TEntity) et tous ses frères et sœurs
  • Implémentation de IQueryable

En plus de traîner avec eux des dépendances inutiles, celles-ci obscurcissent l'intention d'un référentiel qui expose normalement un comportement de collecte très simple. De plus, les abstractions qui fuient sont une tentation constante pour les développeurs de trop se coupler à EF et une menace pour la séparation des préoccupations.

Bottom line: vous devez envelopper ces fatties dans Nice, des concepts rationalisés et devinez quoi, cela signifie introduire des classes supplémentaires.

Un exemple relativement solide de ce que vous pouvez faire avec EF et DDD (bien que certains points de vue exprimés soient discutables): https://kalele.io/blog-posts/modeling-aggregates-with-ddd-and- framework d'entité /

d'autres génèrent décidément des couches supplémentaires, violant même souvent SRP en injectant DbContext dans les racines d'agrégat

Je ne vois vraiment pas le lien entre les deux parties de cette phrase. Quelle que soit l'approche, il existe dans DDD quelque chose appelé Application Service et c'est là que vous manipulez l'unité de travail/référentiel (ou DbContext). Pas dans les racines agrégées.

Bien qu'il puisse s'agir d'une approche valable s'il s'agissait d'un compromis éclairé, la récente tendance anti-référentiel "minimalisme Entity Framework" est délirante. Il blâme les modèles DDD pour la friction qui se produit avec Entity Framework alors que ce sont vraiment les créateurs EF qui n'ont rien fait pour rendre leur cadre conforme aux meilleures pratiques dès le départ. Pendant ce temps, ils sont étroitement liés à ce cadre même avec tous les problèmes de sécurité et de maintenabilité du code qui peuvent en découler.

5
guillaume31

Conflits:

sans implémenter une autre couche de référentiel qui renvoie Aggregate, nous ne pouvons même pas résoudre en partie les pièges susmentionnés

en implémentant une couche supplémentaire de référentiel, nous ignorons les fonctionnalités intégrées d'EF (chaque DbSet est déjà un dépôt) et compliquons trop l'application

J'ai utilisé une approche où chaque agrégat obtient son propre DBContext, mappant exactement ce qui est nécessaire pour l'agrégat. Je pense que cela a également été décrit par Julie Lerman.

Cela a très bien fonctionné, mais pourrait ne pas suffire pour des modèles plus intéressants, où vous ne voulez pas lier vos concepts à vos entités.

2
mvg

Je voudrais juste partager une solution possible pour examen:

  1. éviter de référencer directement le projet EF dans Service Layer

  2. créer une couche de référentiel supplémentaire (utilise le projet EF et retourne la racine agrégée)

  3. référencer le projet Repository Layer in Service Layer

Architecture:

  • UI

  • Couche de contrôleur

  • Couche de service

  • Couche de référentiel

  • Cadre d'entité

  • Projet principal (contient des modèles EF)


Les pièges que je vois avec cette approche:

  • si un référentiel renvoie racine agrégée pas comme arbre de modèle EF (par exemple, nous renvoyons un objet mappé) - nous perdons la capacité d'EF à suivre les modifications

  • si la racine agrégée est un modèle EF - toutes ses propriétés de navigation sont toujours disponibles, même si nous ne pouvons pas traiter DbContext (nous ne référençons pas le projet EF dans Service Layer )

0
Alex Herman