Quand utiliser le modèle de référentiel

20

J'ai lu récemment qu'il n'est pas recommandé d'utiliser le modèle de référentiel conjointement avec un ORM. D'après ma compréhension, c'est parce que l'abstraction qu'ils fournissent sur la base de données SQL est trop fuyante pour être contenue par le modèle.

J'ai quelques questions à ce sujet:

  1. Que faites-vous si vous souhaitez désactiver les ORM? Vous auriez du code spécifique ORM dans votre application si vous ne le contenez pas dans un référentiel.

  2. Le modèle de référentiel est-il toujours valide lorsque vous n'utilisez pas d'ORM et vous utilisez ADO.NET pour l'accès aux données et le remplissage des données d'objet vous-même?

  3. Si vous utilisez un ORM mais pas le modèle de référentiel, où conservez-vous les requêtes couramment utilisées? Serait-il sage de représenter chaque requête en tant que classe et d'avoir une sorte de fabrique de requêtes pour créer des instances?

user1450877
la source
1
1) vous ne pourrez jamais échanger un ORM en raison de leurs comportements différents, même si les deux prennent en charge linq, ils se comporteront suffisamment différemment pour que votre application se casse. par exemple , le comportement de chargement paresseux, suivi sale, proxy fantôme etc .. Il est cependant agréable d'être en mesure d'échanger le ORM pour une mise en œuvre en mem pour tester ..
Roger Johansson
Pour ce que ça vaut, mon point de vue sur la discussion Repository / ORM est ici: stackoverflow.com/questions/13180501/…
Eric King
Où avez-vous lu que c'est une mauvaise pratique?
Dave Hillier

Réponses:

3

1) Vrai, mais à quelle fréquence changez-vous un ORM?
2) Je dirais que oui, car un contexte ORM est une sorte de référentiel et cache beaucoup de travail impliquant la création de requêtes, la récupération de données, le mappage, etc. Si vous n'utilisez pas d'ORM, cette logique doit encore résider quelque part. Je ne sais pas si cela pourrait être qualifié de modèle de référentiel au sens strict du terme ...
3) L'encapsulation des requêtes est quelque chose que je vois fréquemment, mais c'est généralement plus à des fins de test / stubbing. En dehors de cela, je serais prudent lors de la réutilisation d'une requête dans différentes parties d'une application, car vous risquez alors de créer une dépendance sur quelque chose qui pourrait changer n fois (n étant le nombre d'emplacements où vous utilisez la requête).

Stefan Billiet
la source
1
1) Vous posez la mauvaise question. Peu importe la fréquence, cela ne doit être qu'une fois. Étant donné le nombre d'options ORM disponibles, il peut y en avoir une meilleure que celle que vous utilisez déjà. La question devient maintenant: que se passe-t-il lorsque vous le faites? Le référentiel vous donne une belle abstraction. J'y suis allé et j'aimerais avoir fait une telle abstraction en premier lieu.
devnull
3
@devnull je ne suis pas d'accord. Si cela se produisait au plus une fois, je considérerais que c'est un risque acceptable. Si vous craignez de faire le mauvais choix: expérimentez davantage avant de vous engager dans un. En théorie, une telle abstraction semble agréable, mais dans la pratique, vous finissez par recréer une assez grande partie de l'api de votre ORM, tout cela parce que vous pouvez finir par en choisir une autre un jour, quelque part. Pour moi, c'est un effort inutile, un code redondant et plus de code que vous devez vous-même maintenir et garantir. De plus, la commutation d'un ORM ne devrait pas affecter une application entière; apprendre à placer des limites de domaine.
Stefan Billiet
Le changement d'ORM n'est pas la seule raison du dépôt. Si vous devez introduire un cache [distribué] dans votre application - toutes les modifications seront effectuées dans le référentiel et votre BL ne changera pas en raison des changements de couche d'accès aux données.
Valery
le cache distribué ne serait-il pas intégré dans l'ORM dans la plupart des cas?
user1450877
Je pense que cela dépend de l'ORM. Vous pouvez décider d'utiliser un ORM plus léger que NHibernate ou EF.
Valery
2

1) Que faites-vous si vous souhaitez désactiver les ORM, vous auriez un code ORM spécifique dans votre application si vous ne le contenez pas dans un référentiel.

Je n'ai pas encore été dans une position où l'entreprise a soudainement décidé de changer de technologie d'accès aux données. Si cela se produit, des travaux seront nécessaires. J'ai tendance à abstraire les opérations d'accès aux données via des interfaces. Le référentiel est un moyen de résoudre ce problème.

J'aurais alors un assemblage différent pour la mise en œuvre concrète de ma couche d'accès aux données. Par exemple, je pourrais avoir:

Company.Product.Dataet Company.Product.Data.EntityFrameworkassemblages. Le premier assemblage serait utilisé uniquement pour les interfaces, alors qu'un autre serait une implémentation concrète de la logique d'accès aux données d'Entity Framework.

2) Le modèle de référentiel est-il toujours valide lorsque vous n'utilisez pas d'ORM et vous utilisez ADO.net pour l'accès aux données et le remplissage des données d'objet vous-même?

Je pense que c'est à vous de décider quel modèle est valide ou non. J'ai utilisé un modèle de référentiel dans la couche de présentation. Une chose à garder à l'esprit est que les gens aiment jeter des responsabilités dans des référentiels. Avant de vous en rendre compte, votre classe de référentiel dansera, chantera et fera toutes sortes de choses. Vous voulez éviter cela.

J'ai vu une classe de référentiel qui a commencé par avoir des responsabilités GetAll, GetById, Update et Delete, ce qui est bien. Au moment où le projet était terminé, cette même classe avait des dizaines de méthodes (responsabilités) qui n'auraient jamais dû être là. Par exemple GetByForename, GetBySurname, UpdateWithExclusions et toutes sortes de trucs fous.

C'est là que les requêtes et les commandes entrent en jeu.

3) Si vous utilisez un ORM mais pas le modèle de référentiel où conservez-vous les requêtes couramment utilisées. Serait-il sage de représenter chaque requête en tant que classe et d'avoir une sorte de fabrique de requêtes pour créer des instances?

Je pense que c'est une très bonne idée d'utiliser des requêtes et des commandes au lieu de référentiels. Je fais ce qui suit:

  • Définissez l'interface pour une requête. Cela vous aidera à effectuer des tests unitaires. Par exemplepublic interface IGetProductsByCategoryQuery { ... }

  • Définissez une implémentation concrète pour une requête. Vous pourrez les injecter via le cadre IoC de votre choix. Par exemplepublic class GetProductsByCategoryQuery : IGetProductsByCategoryQuery

Maintenant, au lieu de polluer le référentiel avec des dizaines de responsabilités, je regroupe simplement mes requêtes dans des espaces de noms. Par exemple, une interface pour la requête ci-dessus peut vivre: Company.SolutionName.Products.Querieset l'implémentation peut vivre dansCompany.SolutionName.Products.Queries.Implementation

Lorsqu'il s'agit de mettre à jour ou de supprimer des données, j'utilise le modèle de commande de la même manière.

Certains pourraient être en désaccord et dire qu'avant la fin du projet, vous aurez des dizaines de classes et d'espaces de noms. Oui. Dans mon esprit, c'est une bonne chose car vous pouvez parcourir la solution dans l'IDE de votre choix et voir instantanément le type de responsabilités de certains composants. Si vous avez décidé d'utiliser un modèle de référentiel à la place, vous devrez regarder à l'intérieur de chaque classe de référentiel en essayant de déterminer ses responsabilités.

CodeART
la source
J'aime l'idée d'avoir des commandes au lieu de fonctions génériques. Où puis-je en savoir plus sur la façon de les mettre en œuvre dans le contexte de l'accès aux données?
ankush981
1

AVERTISSEMENT: Ce qui suit est basé sur ma compréhension et ma brève expérience dudit modèle (référentiel qui est). Je me trompe peut-être ... en fait, je suis assez certain que je me trompe :). Alors, s'il s'agit d'une tentative de réponse, c'est aussi une question déguisée.

J'utilise le modèle de référentiel pour abstraire la couche d'accès aux données, qui est dans la plupart des cas un ORM. Il s'agit d'une interface générique, avec des méthodes pour créer, lire, mettre à jour, supprimer et implémenter des classes pour LINQ to SQL et EF jusqu'à présent. Je pourrais faire une implémentation qui écrit dans des fichiers XML sur disque (juste un exemple sauvage de ce que je pourrais en faire). Je ne mets pas en œuvre l'unité de travail car elle est prise en charge par les ORM. Si besoin est, je pourrais probablement l'implémenter. Je l'aime comme ça parce que, jusqu'à présent, cela m'a donné une belle séparation. Je ne dis pas qu'il n'y a pas de meilleures alternatives.

Pour répondre à vos questions:

  1. Que cela vous plaise ou non, le changement se produira. Et si vous avez écrit un tas d'applications et que le temps est venu de les maintenir, mais que vous pensez que c'est pénible de travailler avec l'ORM actuel et que vous voulez le changer, vous vous félicitez d'avoir fait une telle abstraction. Utilisez simplement tout ce qui vous convient, que ce soit un référentiel ou un autre modèle / concept.
  2. Oui, tant que vous l'utilisez pour séparer l'accès aux données. Comme je l'ai mentionné précédemment, vous pouvez créer une implémentation qui écrit des données dans des fichiers plats.
  3. J'utilise le référentiel pour conserver mes requêtes et objets de requête couramment utilisés lorsque j'ai des requêtes que je souhaite modifier (qui ne sont pas si nombreuses).

Pour vous donner un autre exemple, les gars d'Umbraco ont retiré le conteneur DI, juste au cas où ils pourraient vouloir passer à autre chose.

devnull
la source
0

Si l'ORM offre la flexibilité de LINQ, un chargement rapide, etc. Je ne le cacherais pas derrière une couche supplémentaire.
S'il s'agit de SQL brut (micro ORM), la "méthode par requête" doit être utilisée de toute façon pour atteindre la réutilisabilité, ce qui rend le modèle de référentiel un bon ajustement.

What do you do if you want to switch out ORMs? 
You would have ORM specific code in your application if you do not contain it in a repository.

Pourquoi auriez-vous besoin de changer?
Celui qui possède la plupart des fonctionnalités dont vous avez besoin doit être utilisé. Il est possible qu'une nouvelle version d'ormX apporte de nouvelles fonctionnalités et s'avère meilleure que la version actuelle, mais ...
Si vous choisissez de masquer l'orm, vous ne pouvez utiliser que les fonctionnalités que tous les candidats ont en commun.
Par exemple, vous ne pouvez pas utiliser de Dictionary<string, Entity>propriétés dans vos entités car ormY ne peut pas les gérer.

En supposant que LINQ est utilisé, la majorité du commutateur orm change simplement les références de bibliothèque et les remplace session.Query<Foo>()par context.Foosou similaire jusqu'à ce qu'il compile. Tâche ennuyeuse, mais prend moins de temps que le codage de la couche d'abstraction.

Is the repository pattern still valid when not using an ORM and you are using ADO.NET for data access and populating object data yourself?

Oui. Le code doit être réutilisable et cela signifie mettre le bâtiment sql, la matérialisation des objets, etc. en un seul endroit (classe séparée). Vous pouvez aussi appeler la classe "XRepository" et en extraire une interface.

If you use an ORM but not the repository pattern where do you keep commonly used queries? 
Would it be wise to represent each query as a class and have some sort of query factory to create instances?

En supposant que LINQ est utilisé, un wrapper de classe serait exagéré à mon humble avis. Une meilleure façon serait les méthodes d'extension

public static IQueryable<T> Published<T>(this IQueryable<T> source) where T : Page
{
    // at some point someone is going to forget to check that status
    // so it makes sense to extract this thing
    return source.Where(x => x.Status == Status.Published && x.PublishTime <= DateTime.UtcNow);
}

Tout code qui est utilisé à plusieurs endroits (ou qui a le potentiel) et qui est suffisamment compliqué (sujet aux erreurs), doit être extrait dans un endroit centralisé.

Mike Koder
la source