36 votes

Modèle de référentiel Normalisation des méthodes

J'essaie simplement de trouver la définition correcte du modèle de référentiel.

Ma compréhension initiale était la suivante (extrêmement simplifiée)

  • Séparer vos objets d'affaires de vos objets de données
  • Normaliser les méthodes d'accès dans la couche d'accès aux données.

J'ai vraiment vu deux mises en œuvre différentes, et il n'y a pas d'exemples formels en ligne, ceux que j'ai vus sont rangés dans des livres.

Mise en œuvre 1 :

public Interface IRepository<T>{
      List<T> GetAll();
      void Create(T p);
      void Update(T p);
}

public interface IProductRepository: IRepository<Product> {
      //Extension methods if needed
       List<Product> GetProductsByCustomerID();
}

Mise en œuvre 2 :

public interface IProductRepository {
      List<Product> GetAllProducts();
      void CreateProduct(Product p);
      void UpdateProduct(Product p);
      List<Product> GetProductsByCustomerID();
}

Remarquez que le premier est un Get/Update/GetAll générique, etc., le second est plus proche de ce que je définirais comme un "DAO".

Les deux partagent une extraction de vos entités de données. Ce qui me plaît, mais je peux faire la même chose avec un simple DAO. Cependant, la deuxième partie standardise les opérations d'accès, je vois de la valeur, si vous implémentez cela à l'échelle de l'entreprise, les gens connaîtront facilement l'ensemble des méthodes d'accès pour votre référentiel.

Ai-je tort de penser que la normalisation de l'accès aux données fait partie intégrante de ce modèle ? Si les deux sont corrects, pourquoi choisir la mise en œuvre 2 ?

Rhino a un bon article sur la mise en œuvre 1, et bien sûr MS a une vague définition et un exemple de mise en œuvre 2 est aquí .

20voto

Johannes Rudolph Points 19845

Je soutiens la citation de Fowler citée par oded. Je tiens à souligner qu'il a dit "collection- comme Interface ". La façon dont vous implémentez l'interface de type collection est certainement à votre discrétion, mais vous ne pouvez ni ne devez essayer de cacher le fait qu'elle représente une source de données distante. Elle diffère donc sensiblement d'une collection en mémoire, qui n'a pas besoin d'intégrer les modifications à un magasin de données distant. Le mécanisme de suivi des modifications de votre ORM ou de votre solution "roll-your-own" détermine la transparence de cette opération pour l'appelant. Les suppressions doivent généralement être marquées explicitement, les insertions peuvent être découvertes (persistance par accessibilité) et les mises à jour doivent parfois être marquées explicitement également. Combinez cela avec les dépendances compliquées de vos racines d'agrégats et vous verrez que cela ne ressemble pas vraiment à une collection.

Il n'existe pas de "mise en œuvre du référentiel canonique".

Il y a une bataille constante entre les partisans d'une classe de base générique pour les référentiels et ceux qui préfèrent implémenter chaque référentiel séparément. Bien que l'implémentation générique soit attrayante dans des scénarios simples, vous trouverez très souvent que c'est une abstraction très peu fiable. Par exemple, certains de vos agrégats ne peuvent être supprimés que de manière douce (cistomisable via des surcharges de méthodes virtuelles) tandis que d'autres ne supportent pas du tout l'opération de suppression.

Assurez-vous de comprendre les implications de chaque approche avant de décider de la voie à suivre. Greg Young a publié un bon article sur les mérites des référentiels génériques.

http://codebetter.com/blogs/gregyoung/archive/2009/01/16/ddd-the-generic-repository.aspx

8voto

Oded Points 271275

D'après Martin Fowler "Patterns of Enterprise Application Architecture", la définition du Repository Pattern est la suivante :

Assure la médiation entre le domaine et les couches de mappage de données en utilisant une interface de type collection pour accéder aux objets du domaine.

Les deux approches sont donc correctes.

4voto

Chris Marisic Points 11495

Je suis un grand fan du modèle de référentiel générique mais je pense que vous devriez fortement envisager de ne pas hériter directement de l'interface car cela peut devenir une limitation très importante, en particulier parce que, bien souvent, le code de l'interface générique sera le même que celui qui pourrait être défini dans une classe de base abstraite que vous ne pourrez plus avoir plus d'un référentiel générique dans une classe.

Je recommande que l'implémenteur de votre IProductRepository accède au générique IRepository<Product> par délégation et l'injecter par le biais du constructeur, de sorte que vous pouvez composer votre classe à partir de plusieurs IRepositories et les regrouper derrière une interface unique d'une manière qui a du sens.

J'ai écrit un blog sur ce sujet ; bien qu'il fasse spécifiquement référence à NHibernate, ce modèle peut être appliqué à tout type de référentiel : Création d'un référentiel commun générique et extensible NHiberate version 2

2voto

Tragedian Points 12308

Avec l'introduction de LINQ dans .NET, un modèle de référentiel générique devient beaucoup plus facile à réaliser :

public interface IRepository<T> : IQueryable<T>
{
    void Add(T item);
    void Remove(T item);
}

Pour être considéré comme un référentiel, il doit simplement être capable d'accéder aux données du magasin sous-jacent (facilement fourni par IQueryable ) et modifier les données contenues.

Vous pouvez fournir des extensions à l'interface de base pour fournir des crochets pour un comportement plus spécifique à l'entité (comme le câblage dans un appel de procédure stockée pour un référentiel basé sur SQL), mais la majorité des opérations peuvent être effectuées par l'interface simple.

1voto

tijmenvdk Points 1468

En plus de votre interface de référentiel générique (implémentation 1) et de votre variation sur le référentiel spécifique au rôle (implémentation 2), vous pouvez également envisager un référentiel de méthodes générique :

public interface IRepository
{
    void Save<ENTITY>(ENTITY entity) where ENTITY : DomainEntity;

    ENTITY Load<ENTITY>(Guid id) where ENTITY : DomainEntity;

    IQueryable<ENTITY> Query<ENTITY>() where ENTITY : DomainEntity;

    IQueryable<ENTITY> Query<ENTITY>(IDomainQuery<ENTITY> whereQuery)
        where ENTITY : DomainEntity;
}

Cette troisième version provient de ce blogpost de Jimmy Bogard, où il exprime également sa préférence pour l'interface de référentiel générique. J'ai l'habitude de la suivre avec une classe de base de référentiel générique qui implémente cette interface ; de cette façon, je n'ai à implémenter que les éléments qui sont différents pour chaque entité de domaine.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X