Шаблон репозитория Стандартизация методов

Все пытаюсь выяснить правильное определение паттерна репозитория.

Мое первоначальное понимание было таким (чрезвычайно тупым)

  • Отделите свои бизнес-объекты от объектов данных
  • Стандартизируйте методы доступа на уровне доступа к данным.

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

Реализация 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();
}

Реализация 2:

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

Обратите внимание, что первый - это общий Get / Update / GetAll и т. Д., Второй - это больше похоже на то, что я бы определил как «DAO».

Оба разделяют извлечение из ваших объектов данных. Что мне нравится, но я могу сделать то же самое с простым DAO. Однако вторая часть стандартизирует операции доступа, в которых я вижу ценность, если вы реализуете это в масштабе предприятия, люди легко узнают набор методов доступа для вашего репозитория.

Я ошибаюсь, полагая, что стандартизация доступа к данным является неотъемлемой частью этого шаблона? Если оба верны, почему следует выбрать реализацию 2?

Rhino имеет хорошую статью о реализации 1, и, конечно же, у MS есть расплывчатое определение и пример реализации. 2 находится здесь.


person Nix    schedule 21.04.2010    source источник
comment
Для меня интерфейс - это абстракция, т.е. противоположность реализации. Обсуждаем ли мы здесь только интерфейсы или, скорее, реализующие классы?   -  person MEMark    schedule 12.10.2013


Ответы (7)


Я поддерживаю цитату Фаулера, процитированную одедом. Хочу отметить, что он сказал интерфейс "коллекция- как". Как вы реализуете интерфейс, подобный коллекции, определенно зависит от вас, но вы не можете и не должны пытаться скрыть тот факт, что он представляет собой удаленный источник данных. Таким образом, он значительно отличается от коллекции в памяти, которая не требует сброса изменений в удаленное хранилище данных. Механизм отслеживания изменений вашего ORM или вашего собственного решения определяет, насколько прозрачным это может быть сделано для вызывающего абонента. Удаление обычно необходимо отмечать явно, вставки доступны для обнаружения (постоянство по достижимости), а обновления иногда также необходимо отмечать явно. Объедините это со сложными зависимостями ваших совокупных корней, и вы увидите, что это не очень похоже на коллекцию.

Не существует такой вещи, как «реализация канонического репозитория».

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

Убедитесь, что вы понимаете значение каждого подхода, прежде чем решать, какой путь выбрать. У Грега Янга хороший пост о достоинствах универсальных репозиториев.

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

person Johannes Rudolph    schedule 27.04.2010
comment
Вся суть репозитория в том, чтобы абстрагироваться от концепций, связанных с персистентностью. Некоторые из них обычно помещаются в единицу работы, которую нужно использовать только в вашей инфраструктуре для автоматической очистки изменений после того, как код вашего приложения обработает запрос. Если ваш репозиторий выглядит как DAO, вы просто делаете DAO снова и снова, а это не главное. - person pnschofield; 01.06.2018

Из книги Мартина Фаулера «Шаблоны архитектуры корпоративных приложений» определение шаблона репозитория:

Осуществляет посредничество между уровнями отображения домена и данных, используя интерфейс, подобный коллекции, для доступа к объектам домена.

Итак, оба подхода верны.

person Oded    schedule 21.04.2010
comment
я полагаю, что вся коллекция похожа на расплывчатую, я бы сказал, что эта коллекция как бы означала Get / Update / Remove / и т. д., но я предполагаю, что вы будете утверждать то же самое, что Get = GetProduct - person Nix; 21.04.2010

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

Я рекомендую вашему разработчику IProductRepository получить доступ к общему IRepository<Product> через делегирование и ввести его через конструктор, чтобы вы могли составить свой класс, возможно, из многих IRepositories, и сгруппировать их за одним интерфейсом таким образом, который имеет смысл.

Я написал блог на эту тему, хотя в нем конкретно упоминается NHibernate, этот шаблон можно применить к любому типу репозитория: Создание общего универсального и расширяемого репозитория NHiberate версии 2

person Chris Marisic    schedule 21.04.2010

С появлением LINQ в .NET стало намного проще реализовать общий шаблон репозитория:

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

Чтобы квалифицироваться как репозиторий, ему просто нужно иметь возможность доступа к данным в базовом хранилище (легко предоставляется IQueryable) и изменять содержащиеся данные.

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

person Paul Turner    schedule 27.04.2010

В дополнение к вашему универсальному интерфейсу репозитория (реализация 1) и вашему варианту репозитория для конкретных ролей (реализация 2) вы также можете рассмотреть универсальный репозиторий методов:

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

Эта третья версия взята из этого blogpost Джимми Богарда, где он также выражает предпочтение общему интерфейсу репозитория. Я обычно следую этому с помощью базового класса базового репозитория, который реализует этот интерфейс; таким образом, мне нужно реализовать только те вещи, которые различны для каждого объекта домена.

person tijmenvdk    schedule 27.04.2010

Я обычно использую общий репозиторий с композицией вместо наследования. Это дает мне преимущество общей реализации с контролем того, какие методы открывать.

Что-то вроде этого:

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


public interface IProductRepository {
  //Extension methods if needed
   List<Product> GetProductsByCustomerID();
   List<T> GetAll();
   void Create(T p);
   //Let assume here you should not be able to update the products
}

public ProductRepository : IProductRepository {
    private IRepository _repository;

    public ProductRepository(IRepository repository) {
        this._repository = repository;
    }

       List<T> GetAll() 
       {
            _repository.GetAll();
       }

       void Create(T p) 
       {
            _repository.Create(p);
       }

       List<Product> GetProductsByCustomerID() 
       {
          //..implementation goes here
       }
}
person user0b101010    schedule 25.11.2011

Шаблон репозитория - один из наиболее часто используемых при разработке программного обеспечения. Есть много сообщений, которые можно отметить как ответ на ваш вопрос. Мне нравится выделять то, что хорошая реализация репозитория будет улучшена, если вы будете использовать IoC (Autofac, Windsor и т. Д.). Я давно играл с некоторыми фреймворками на основе ADO.NET (LinqToSql, EF) и NHibernate. Вы всегда можете получить преимущества от универсальной реализации, если используете IoC. Вы можете определить интерфейсы для ваших конкретных репозиториев и решить, когда вам действительно нужны определенные действия.

person Miguel    schedule 31.07.2013