Вопрос

Я изучал 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) Моя наименее любимая, но, возможно, самая прямая реализация и, возможно, наиболее эффективная база данных для сложных запросов, хотя это накладывает большую ответственность на вызывающий код.

Это было полезно?

Решение

Я думаю, что все они являются хорошими вариантами (за исключением, может быть, 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);
}

И есть также базовый класс репозитория (T).

Другие советы

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

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

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

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

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

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

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

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

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

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

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

session.Linq<Entity>()

Спецификации - это то, с чем связано:

IQueryable<Entity>

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

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

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

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

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

Увядание репозитория

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

http: // www .lostechies.com / блоги / jimmy_bogard / Архив / 2009/09/10 / засыхают-The-repository.aspx

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top