Question

J’ai exploré BDD / DDD et, par conséquent, essayé d’implémenter correctement le modèle de référentiel. Jusqu'à présent, il était difficile de trouver un consensus sur la meilleure façon de mettre en œuvre cela. J'ai essayé de résumer le problème aux variations suivantes, mais je ne sais pas quelle est la meilleure approche.

Pour référence, je construis une application ASP.MVC avec NHibernate en tant que back-end.

public interface IRepository<T> {
        // 1) Thin facade over LINQ
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IQueryable<T> Find();
        // or possibly even
        T Get(Expression<Func<T, bool>> query);
        List<T> Find(Expression<Func<T, bool>> query);
}

public interface IRepository<T> {
        // 2) Custom methods for each query
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> FindBySku(string sku);
        IList<T> FindByName(string name);
        IList<T> FindByPrice(decimal price);
        // ... and so on
}

public interface IRepository<T> {
        // 3) Wrap NHibernate Criteria in Spec pattern
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> FindBySpec(ISpecification<T> specification);
        T GetById(int id);
}


public interface IRepository<T> {
        // 4) Expose NHibernate Criteria directly
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> Find(ICriteria criteria);
        // .. or possibly
        IList<T> Find(HQL stuff);
}

Mes pensées initiales sont que

1) est excellent du point de vue de l'efficacité, mais je risque d'avoir des problèmes lorsque les choses se compliquent.

2) semble très fastidieux et pourrait se retrouver avec une classe très encombrée, mais offre par ailleurs un degré élevé de séparation entre la logique de mon domaine et la couche de données que j’aime.

3) semble difficile au début et demande plus de travail pour écrire des requêtes, mais limite la contamination croisée au seul calque Specs.

4) Ma moins préférée, mais peut-être la plus directe implémentation et probablement la plus efficace des bases de données pour les requêtes complexes, bien que cela implique beaucoup de responsabilité sur le code d'appel.

Était-ce utile?

La solution

Je pense que ce sont toutes de bonnes options (sauf peut-être 4 si vous ne voulez pas vous lier), et vous semblez bien analyser le pour et le contre pour prendre vous-même une décision en fonction de votre projet actuel. Ne vous battez pas trop trop à ce sujet.

Je travaille actuellement sur un mélange entre 2 et 3, je suppose:

public interface IRepository<T> 
{
        ...
        IList<T> FindAll();
        IList<T> FindBySpec(ISpecification<T> specification);
        T GetById(int id);
}

public interface ISpecificRepository : IRepository<Specific> 
{
        ...
        IList<Specific> FindBySku(string sku);
        IList<Specific> FindByName(string name);
        IList<Specific> FindByPrice(decimal price);
}

Et il y a aussi une classe de base Repository (de T).

Autres conseils

Il existe également un bon argument en faveur d'un & "aucune de ces réponses &"; approche.

Le problème des référentiels génériques est que vous supposez que tous les objets de votre système prendront en charge les quatre opérations CRUD: Créer, Lire, Mettre à jour, Supprimer. Mais dans les systèmes complexes, vous aurez probablement des objets qui ne prennent en charge que quelques-unes des opérations. Par exemple, vous pouvez avoir des objets en lecture seule ou des objets créés mais jamais mis à jour.

Vous pouvez diviser l’interface IRepository en petites interfaces, telles que Lecture, Suppression, etc., mais cela se complique assez rapidement.

Gregory Young avance un bon argument (du point de vue de la superposition DDD / logiciel) selon lequel chaque référentiel ne devrait prendre en charge que les opérations spécifiques à l’objet ou à l’agrégat avec lequel vous travaillez. Voici son article sur les dépôts génériques .

Et pour une autre vue, voyez ceci Ayende article de blog .

L’une des choses que nous faisons est que tous nos référentiels ont des besoins différents, nous créons donc une collection d’interfaces:

public interface IReadOnlyRepository<T,V>
{
   V Find(T);
}

Dans cet exemple, le référentiel en lecture seule provient simplement de la base de données. La raison de T, V est que V représente ce qui est renvoyé par le référentiel et que T représente ce qui est passé, vous pouvez donc faire quelque chose comme ceci:

public class CustomerRepository:IReadOnlyRepository<int, Customer>, IReadOnlyRepository<string, Customer>
{
    public Customer Find(int customerId)
    {
    }

    public Customer Find(string customerName)
    {
    }
}

Je peux également créer des interfaces distinctes pour l’ajout, la mise à jour et la suppression. Ainsi, si mon référentiel n’a pas besoin de ce comportement, il n’implémente pas l’interface.

Je suis un fan bif de 1 car je peux créer des filtres et des méthodes d'extension de pagination que je ne peux appliquer à IQueryable < > renvoie des valeurs pour la méthode Find. Je conserve les méthodes d'extension dans la couche de données, puis je les construis à la volée dans la couche de gestion. (Pas tout à fait pur, certes.)

Bien sûr, lorsque le système se stabilise, j'ai la possibilité de créer des méthodes de recherche spécifiques utilisant les mêmes méthodes d'extension et d'optimiser l'utilisation de Func < >.

.

Lorsque vous utilisez NH avec Linq, votre référentiel peut être:

session.Linq<Entity>()

Les spécifications sont des éléments qui traitent:

IQueryable<Entity>

Vous pouvez masquer tout cela si vous le souhaitez, mais cela représente beaucoup de travail banal d'abstraction.

Simple, c'est bien. Yah, NH fait des bases de données, mais il fournit beaucoup plus de modèles. Avoir autre chose que le DAL à dépendre de NH est loin d'être un péché.

Je pense que cela dépend de vos besoins. Il est important de penser au référentiel en conjonction avec d'autres modèles de conception que vous envisagez d'utiliser. Enfin, tout dépend de ce que vous attendez du référentiel (quelles sont les principales raisons de l’utiliser).

Devez-vous créer une couche stricte (par exemple, vous devrez remplacer NHibernate par Entity Framework à l’avenir)? Voulez-vous écrire test en particulier sur les méthodes du référentiel?

Il n’existe pas de meilleur moyen de créer un référentiel. Il n’ya que quelques moyens et c’est à vous de choisir celui qui vous convient le mieux.

Wither le référentiel

Un article pertinent de Jimmy Bogard de LosTechies

http: // www .lostechies.com / blogs / jimmy_bogard / archive / 2009/09/10 / wither-the-repository.aspx

De plus, un autre article rapide avec des commentaires suggérant que la version 2 est vraiment un modèle DOA et non un référentiel.

http://fabiomaulo.blogspot.com/2009/06 /linq-and-repository.html

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top