Лучший способ реализовать шаблон репозитория? - PullRequest
35 голосов
/ 11 сентября 2009

Я изучал BDD / DDD и, как следствие, пытался придумать правильную реализацию шаблона Repository. До сих пор было трудно найти консенсус в отношении наилучшего способа реализации этого. Я пытался свести его к следующим вариантам, но я не уверен, что это лучший подход.

Для справки я создаю приложение ASP.MVC с NHibernate в качестве бэк-энда.

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);
}

Мои первоначальные мысли таковы:

1) замечательно с точки зрения эффективности, но у меня могут возникнуть проблемы, когда все станет сложнее.

2) кажется очень утомительным и может привести к очень переполненному классу, но в остальном предлагает высокую степень разделения между моей логикой предметной области и уровнем данных, который мне нравится.

3) кажется трудным заранее и требует дополнительной работы для написания запросов, но ограничивает перекрестное загрязнение только слоем Specs.

4) Моя наименее любимая, но, возможно, самая прямая реализация и, возможно, наиболее эффективная база данных для сложных запросов, хотя это накладывает большую ответственность на вызывающий код.

Ответы [ 7 ]

22 голосов
/ 12 сентября 2009

Есть также хороший аргумент для подхода «ни один из вышеперечисленных».

Проблема с общими репозиториями заключается в том, что вы предполагаете, что все объекты в вашей системе будут поддерживать все четыре операции CRUD: создание, чтение, обновление, удаление. Но в сложных системах у вас, скорее всего, будут объекты, которые поддерживают только несколько операций. Например, у вас могут быть объекты, доступные только для чтения, или объекты, которые созданы, но никогда не обновляются.

Вы можете разбить интерфейс IRepository на маленькие интерфейсы для чтения, удаления и т. Д., Но это довольно быстро запутывается.

Грегори Янг приводит хороший аргумент (с точки зрения DDD / программной многоуровневости), что каждый репозиторий должен поддерживать только те операции, которые характерны для объекта домена или агрегата, с которым вы работаете. Вот его статья о универсальных репозиториях .

А для альтернативного просмотра см. Этот пост в блоге Ayende .

10 голосов
/ 11 сентября 2009

Я думаю, что все они являются хорошими вариантами (за исключением, может быть, 4, если вы не хотите привязывать себя к nhibernate), и вы, похоже, хорошо проанализировали плюсы и минусы, чтобы самостоятельно принять решение на основе ваших текущих усилий. Не бей себя слишком сильно на этом.

Я сейчас работаю над смесью между 2 и 3, я думаю:

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);
}

И есть также базовый класс Repository (of T).

6 голосов
/ 11 сентября 2009

Одна из вещей, которые мы делаем, состоит в том, что все наши репозитории имеют разные потребности, поэтому мы создаем коллекцию интерфейсов:

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

В этом примере репозиторий только для чтения получает из базы данных. Причиной T, V является то, что V представляет то, что возвращается хранилищем, а T представляет то, что передается, так что вы можете сделать что-то вроде этого:

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

    public Customer Find(string customerName)
    {
    }
}

Я также могу создавать отдельные интерфейсы для добавления, обновления и удаления. Таким образом, если мой репозиторий не нуждается в поведении, он просто не реализует интерфейс.

1 голос
/ 07 сентября 2015

Я считаю, что это зависит от ваших потребностей. Важно думать о хранилище в сочетании с другими шаблонами проектирования, которые вы планируете использовать. Наконец, это очень зависит от того, что вы ожидаете от хранилища (каковы основные причины его использования).

Вам нужно создать строгий слой (например, вам нужно будет заменить NHibernate на Entity Framework в будущем)? Вы хотите написать тест, особенно для методов хранилища?

Нет лучшего способа создания репозитория. Есть только несколько способов, и это определенно зависит от вас, что является наиболее практичным для ваших нужд.

1 голос
/ 11 сентября 2009

При использовании NH с Linq ваш репозиторий может быть:

session.Linq<Entity>()

Спецификации - это вещи, которые имеют дело с:

IQueryable<Entity>

Вы можете скрыть все это, если хотите, но это много мирской работы, чтобы абстрагировать абстракцию.

Простое - это хорошо. Да, NH делает базы данных, но предоставляет гораздо больше шаблонов. Наличие иного, чем DAL, зависит от NH, далеко не грех.

1 голос
/ 11 сентября 2009

Я фанат bif, равный 1, потому что я могу создавать фильтры и методы расширения подкачки, а не применять к возвращаемым значениям IQueryable <> для метода Find. Я сохраняю методы расширения на уровне данных, а затем строю на лету на бизнес-уровне. (Не совсем чистый, по общему признанию.)

Конечно, когда система стабилизируется, у меня есть возможность создавать определенные методы Find, используя те же методы расширения, и оптимизировать, используя Func <>.

0 голосов
/ 11 сентября 2009

Увядание хранилища

Соответствующая статья Джимми Богарда из LosTechies

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

Кроме того, еще одна быстрая статья с комментариями, предлагающими версию №2, на самом деле является шаблоном DOA, а не репозиторием.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...