Modèles de conception que j'évite souvent: modèle de référentiel

Les modèles de conception fournissent des solutions éprouvées aux problèmes du monde réel rencontrés dans la conception de logiciels. Le modèle de référentiel est utilisé pour découpler la logique métier et les couches d'accès aux données dans votre application.

La couche d'accès aux données contient généralement un code et des méthodes spécifiques au stockage pour opérer sur les données vers et depuis le stockage de données. La couche d'accès aux données que le référentiel résume peut être un ORM (c'est-à-dire, Entity Framework ou NHibernate), un fichier XML, un service Web, etc. Il peut même s'agir d'une collection d'instructions SQL.

Lors de l'utilisation du modèle de conception de référentiel, la couche de logique métier de votre application n'a pas besoin de savoir comment la persistance des données se produit en dessous. Essentiellement, un référentiel sert d'intermédiaire entre le domaine et les couches de mappage de données de votre application. Il est censé vous fournir une encapsulation sur la manière dont les données sont réellement conservées dans la couche de stockage de données.

Le modèle de référentiel peut être utile lorsque vous avez de nombreuses entités et de nombreuses requêtes complexes pour travailler avec ces entités. Dans ce cas, une couche supplémentaire d'abstraction peut vous aider à éliminer la duplication de la logique de requête.

Le référentiel générique

Un référentiel générique est un type qui comprend un ensemble de méthodes génériques pour effectuer des opérations CRUD. Cependant, il s'agit simplement d'un autre motif anti et est fréquemment utilisé avec Entity Framework pour abstraire les appels à la couche d'accès aux données. À mon avis, utiliser un référentiel générique est trop généralisé. C'est une mauvaise idée d'abstraire les appels à Entity Framework à l'aide d'un référentiel générique.

Laissez-moi vous expliquer cela avec un exemple.

La liste de codes suivante illustre un référentiel générique - il contient des méthodes génériques pour effectuer les opérations CRUD de base.

public interface IRepository

   {

       IEnumerable GetAll();

       T GetByID(int id);

       void Add(T item);

       void Update(T item);

       void Delete(T item);

   }

Pour créer un référentiel spécifique, vous devez ensuite implémenter l'interface générique comme indiqué dans la liste de code ci-dessous.

public class AuthorRepository : IRepository

   {

       //Implemented methods of the IRepository interface

   }

Comme vous pouvez le voir, pour créer une classe de référentiel spécifique, vous devez implémenter chacune des méthodes de l'interface de référentiel générique. L'inconvénient majeur de cette approche est que vous devrez créer un nouveau référentiel pour chaque entité.

Voici un autre inconvénient de cette approche: l'intention de base du modèle de référentiel est de découpler votre couche de domaine de la façon dont les données sont réellement conservées par la couche d'accès aux données. Voici une version mise à jour de la classe de référentiel que nous venons de créer.

public class AuthorRepository : IRepository

   {

       private AuthorContext dbContext;

       //Methods of the IRepository interface

   }

Comme vous pouvez le voir dans la liste de codes donnée précédemment, AuthorRepository a besoin de l'instance AuthorContext pour effectuer les opérations CRUD auxquelles il est destiné. Alors, où est le découplage? Idéalement, la couche de domaine ne doit pas avoir de connaissances sur la logique de persistance.

Une couche supplémentaire d'abstraction

Le modèle de domaine et le modèle de persistance dans une application ont des responsabilités distinctes. Alors que le premier modélise le comportement, c'est-à-dire modélise les problèmes réels et les solutions à ces problèmes, le second est utilisé pour modéliser la manière dont les données de l'application sont réellement stockées dans le magasin de données.

L'intention du modèle de référentiel doit être d'abstraire la logique de persistance et de masquer les implémentations internes de la persistance des données. Les opérations du référentiel doivent être suffisamment expressives et non génériques. Vous ne pouvez pas avoir de référentiel générique et pouvant contenir des opérations pouvant s'intégrer dans n'importe quel scénario. Cela devient une abstraction inutile et fait donc du modèle de référentiel générique un anti-modèle. Vous pouvez modéliser tous les objets de votre domaine de la même manière. Un référentiel générique ne définit pas un contrat significatif et vous auriez à nouveau besoin d'un référentiel spécifique qui étend votre référentiel générique et fournit l'ensemble spécifique d'opérations qui sont significatives pour cette entité particulière.

Maintenant que vous disposez de quelques technologies matures de persistance des données (NHibernate, Entity Framework, etc.), pourquoi avez-vous besoin de cette couche supplémentaire d'abstraction de toute façon? La plupart des technologies ORM matures disponibles aujourd'hui ont les mêmes capacités. En essayant d'utiliser un référentiel, vous ajoutez simplement une couche supplémentaire d'abstraction sans aucune raison. Par exemple, vous pourriez avoir besoin de méthodes telles que les suivantes pour votre AuthorRepository.

FindAuthorById()

FindAuthorByCountry()

Cela s'aggrave à mesure que vous avez de plus en plus de méthodes et de recherches complexes - vous finiriez par avoir un référentiel qui correspondrait étroitement à la couche de stockage persistante utilisée en dessous.