(Remarque: ma question a des préoccupations très similaires à celles de la personne qui a posé la question cette question } il y a trois mois, mais aucune réponse n'a été donnée.)
J'ai récemment commencé à travailler avec MVC3 + Entity Framework et je continue de lire que la meilleure pratique consiste à utiliser le modèle de référentiel pour centraliser l'accès au DAL. Ceci est également accompagné d'explications selon lesquelles vous souhaitez que le DAL soit séparé du domaine et en particulier du calque de vue. Mais dans les exemples que j'ai vus, le référentiel est (ou semble être ) renvoyant simplement des entités DAL, c'est-à-dire que dans mon cas, le référentiel renverrait des entités EF.
Ma question est donc la suivante: à quoi sert le référentiel s'il ne renvoie que des entités DAL? Cela n’ajoute-t-il pas une couche de complexité qui n’élimine pas le problème du transfert d’entités DAL entre couches? Si le modèle de référentiel crée un "point d'entrée unique dans le DAL", en quoi est-il différent de l'objet context? Si le référentiel fournit un mécanisme pour extraire et conserver les objets DAL, en quoi est-il différent de l'objet context?
De plus, j'ai lu à au moins un endroit que le modèle d'Unité de travail centralisait l'accès au référentiel afin de gérer le ou les objets de contexte de données, mais je ne comprends pas pourquoi c'est aussi important.
Je suis sûr à 98,8% que quelque chose me manque, mais de mes lectures, je ne l'ai pas vu. Bien sûr, je ne lis peut-être pas les bonnes sources ...: \
La variable DbContext
d'Entity Framework ressemble en gros à un référentiel (ainsi qu'à une unité de travail). Vous n'avez pas nécessairement à faire abstraction dans des scénarios simples.
Le principal avantage du référentiel est que votre domaine peut être ignorant et indépendant du mécanisme de persistance. Dans une architecture basée sur une couche, les dépendances vont de la couche d'interface utilisateur au travers du domaine (ou généralement appelée couche de logique métier) vers la couche d'accès aux données. Cela signifie que l'interface utilisateur dépend de la BLL, qui elle-même dépend du DAL.
Dans une architecture plus moderne (telle que générée par la conception pilotée par le domaine et d'autres approches orientées objet), le domaine ne devrait avoir aucune dépendance vers l'extérieur. Cela signifie que l'interface utilisateur, le mécanisme de persistance et tout le reste devraient dépendre du domaine et non l'inverse.
Un référentiel sera alors représenté via son interface à l'intérieur du domaine mais aura son implémentation concrète à l'extérieur du domaine, dans le module de persistance. De cette façon, le domaine ne dépend que de l'interface abstraite, pas de la mise en œuvre concrète.
Il s’agit essentiellement d’une programmation orientée objet ou procédurale au niveau architectural.
Voir également le Ports et adaptateurs a.k.a. Architecture hexagonale .
Un autre avantage du référentiel est que vous pouvez créer des mécanismes d'accès similaires à différentes sources de données. Non seulement aux bases de données, mais également aux magasins en nuage, aux API externes, aux applications tierces, etc.
Je pense que le terme "référentiel" est couramment utilisé dans la manière dont le " référentiel modèle " est décrit dans le livre Patterns of Enterprise Application Architecture de Martin Fowler.
Un référentiel sert de médiateur entre les couches de mappage de domaine et de données, Agissant comme une collection d'objets de domaine en mémoire. Les objets client Construisent les spécifications de requête de manière déclarative et les soumettent à Repository pour satisfaction. Les objets peuvent être ajoutés et supprimés de Du référentiel, comme ils le peuvent d'une simple collection d'objets, et Le code de mappage encapsulé par le référentiel effectuera les opérations appropriées. Dans les coulisses.
En apparence, Entity Framework accomplit tout cela et peut être utilisé comme une simple forme de référentiel. Cependant, un référentiel peut comporter plus qu'une simple abstraction de couche de données.
Selon le livre Domain Driven Design de Eric Evans, un référentiel présente les avantages suivants:
- Ils présentent aux clients un modèle simple pour obtenir des objets de persistance et gérer leur cycle de vie.
- Ils dissocient la conception d'applications et de domaines de la technologie de persistance, de plusieurs stratégies de base de données ou même de plusieurs sources de données.
- Ils communiquent les décisions de conception concernant l'accès aux objets
- Ils permettent de remplacer facilement une implémentation factice pour les tests unitaires (généralement à l'aide d'une collection en mémoire).
Le premier point équivaut approximativement au paragraphe ci-dessus, et il est facile de voir que Entity Framework lui-même le réalise facilement.
Certains diront que EF accomplit également le deuxième point. Mais couramment, EF sert simplement à transformer chaque table de base de données en une entité EF et à la transmettre à l'interface utilisateur. Il s’agit peut-être d’abstraire le mécanisme d’accès aux données, mais c’est loin d’abstraire la structure de données relationnelle en coulisse.
Dans les applications plus simples principalement orientées données, cela peut sembler ne pas être un point important. Mais à mesure que les règles de domaine et la logique métier des applications deviennent plus complexes, vous souhaiterez peut-être être plus orienté objet. Il n'est pas rare que la structure relationnelle des données contienne des idiosyncrasies qui ne sont pas importantes pour le domaine métier, mais qui sont des effets secondaires du stockage de données. Dans de tels cas, il ne suffit pas d’abstraire le mécanisme de persistance, mais aussi la nature même de la structure de données. EF seul ne vous aidera généralement pas à faire cela, mais une couche de référentiel le fera.
En ce qui concerne le troisième avantage, EF ne fera rien (du point de vue de DDD) pour vous aider. En règle générale, DDD utilise le référentiel non seulement pour résumer le mécanisme de persistance des données, mais également pour définir des contraintes sur la manière d'accéder à certaines données:
Nous n'avons également besoin d'aucun accès aux requêtes pour les objets persistants qui sont plus pratiques à trouver par parcours. Par exemple, l'adresse d'une personne Peut être demandée à partir de l'objet Personne. Et le plus important, aucun objet Interne à un AGREGAT n’est interdit d’accès que par À partir de la racine.
En d'autres termes, vous n'auriez pas de 'AddressRepository' simplement parce que vous avez une table Address dans votre base de données. Si votre conception choisit de gérer le mode d'accès aux objets Adresse de cette manière, c'est dans PersonRepository que vous définirez et appliquerez le choix de conception.
En outre, un référentiel DDD serait généralement constitué de certains concepts métier relatifs à des ensembles de données de domaine. Un référentiel de commandes peut avoir une méthode appelée OutstandingOrdersForAccount qui renvoie un sous-ensemble spécifique de commandes. Ou un référentiel client peut contenir une méthode PreferredCustomerByPostalCode.
Les classes DataContext d'Entity Framework ne se prêtent pas bien à une telle fonctionnalité sans la couche d'abstraction de référentiel ajoutée. Ils fonctionnent bien pour ce que DDD appelle Spécifications, qui peuvent être de simples expressions booléennes envoyées à une méthode simple qui évaluera les données par rapport à l’expression et renverra une correspondance.
En ce qui concerne le quatrième avantage, bien que je sois sûr que certaines stratégies permettent de remplacer le contexte de données, le placer dans un référentiel le rend tout simple.
En ce qui concerne «Unité de travail», voici ce que dit le livre de DDD:
Laissez le contrôle de la transaction au client. Bien que le REPOSITORY insère et supprime dans la base de données, il ne commet normalement rien . Il est tentant de s’engager après la sauvegarde, par exemple, Mais le client a vraisemblablement le contexte pour initier et Engager correctement des unités de travail. La gestion des transactions sera plus simple si le REPOSITORY ne s’arrête pas.
Vous avez raison, dans ces cas simples, le référentiel est simplement un autre nom pour un DAO et il n'apporte qu'une valeur: le fait que vous puissiez passer de EF à une autre technique d'accès aux données. Aujourd'hui, vous utilisez MSSQL, demain, vous aurez besoin d'un stockage en nuage. OR en utilisant un micro orm au lieu de EF ou en passant de MSSQL à MySql.
Dans tous ces cas, il est bon que vous utilisiez un référentiel, car le reste de l'application ne se souciera pas du stockage que vous utilisez actuellement.
Il existe également le cas limité où vous obtenez des informations provenant de plusieurs sources (système de fichiers db +): un référentiel servira de façade, mais il s’agit toujours d’un autre nom pour un DAO.
Un référentiel 'réel' n'est valide que lorsque vous traitez avec des objets de domaine/métier. Pour les applications centrées sur les données qui ne changent pas de stockage, l'ORM seul suffit.
Cela serait utile lorsque vous avez plusieurs sources de données et que vous souhaitez y accéder en utilisant une stratégie de codage cohérente.
Par exemple, vous pouvez avoir plusieurs modèles de données EF et accéder à certaines données à l’aide d’ADO.NET traditionnel avec des procs stockées, et à certaines données à l’aide d’une API tierce, et à accéder à une base de données Access située sur un serveur Windows couverture de poussière dans votre placard à balai.
Vous ne souhaitez peut-être pas que vos couches métier ou front-end se soucient de l'origine des données; vous devez donc créer un modèle de référentiel générique pour accéder aux "données", plutôt que pour les "données Entity Framework".
Dans ce scénario, les implémentations réelles de votre référentiel seront différentes les unes des autres, mais le code qui les appelle ne connaît pas la différence.
Compte tenu de votre scénario, je choisirais simplement un ensemble d'interfaces qui représentent les structures de données (vos modèles de domaine) qui doivent être renvoyées à partir de votre couche de données. Votre implémentation peut alors être un mélange de EF, ADO.Net Raw ou de tout autre type de magasin de données/fournisseur. La stratégie principale consiste à soustraire l'implémentation du consommateur immédiat - votre couche de domaine. Ceci est utile lorsque vous souhaitez tester en bloc vos objets de domaine et, dans des cas moins courants, modifier votre fournisseur de données/votre plate-forme de base de données.
Si vous ne l'avez pas déjà fait, vous devriez envisager d'utiliser un IOC conteneur } _ car ils facilitent très facilement le couplage en vrac de votre solution au moyen de injection de dépendance . _ { Il y en a beaucoup de disponibles }, personnellement, je préfère Ninject .
La couche de domaine doit englober toute votre logique métier - les règles et les exigences du domaine en question, et can être consommé directement par votre application Web MVC3. Dans certaines situations, il est judicieux d'introduire une couche de services située au-dessus de la couche de domaine, mais cela n'est pas toujours nécessaire et peut s'avérer trop lourde pour des applications Web simples.
Une autre chose à considérer est que même lorsque vous savez que vous travaillerez avec un seul magasin de données, il peut être judicieux de créer une abstraction de référentiel. La raison en est que votre application a peut-être besoin d'une fonction que votre ORM du jour fait mal (performances), pas du tout, ou vous ne savez tout simplement pas comment faire en sorte que l'ORM s'adapte à vos besoins.
Si vous placez votre ORM derrière une interface de référentiel bien pensée, vous pouvez facilement basculer entre différentes technologies à votre guise. Il n’est pas rare dans mes dépôts de voir certaines méthodes utiliser EF pour leur travail et d’autres utiliser quelque chose comme PetaPoco ou le code (gasp) ADO.net. L'abstraction de référentiel vous permet d'utiliser exactement le bon outil pour le travail à effectuer, sans fuir ces complexités dans le code client.
Je pense qu'il y a un gros malentendu sur ce que beaucoup d'articles appellent "référentiel". Et c'est pourquoi on doute de la valeur réelle de ces abstractions.
À mon avis, le référentiel dans sa forme pure est IEnumerable, alors que vous et de nombreux articles parlez de "service d'accès aux données".
J'ai blogué à ce sujet ici .