Skip to content

Instantly share code, notes, and snippets.

@maestrow
Last active April 14, 2024 08:22
Show Gist options
  • Star 36 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save maestrow/594fd9aee859c809b043 to your computer and use it in GitHub Desktop.
Save maestrow/594fd9aee859c809b043 to your computer and use it in GitHub Desktop.
Паттерн репозиторий

Паттерн репозиторий

Репозиторий - это слой абстракции, инкапсулирующий в себе всё, что относится к способу хранения данных. Назначение: Разделение бизнес-логики от деталей реализации слоя доступа к данным.

Паттерн Репозиторий стал популярным благодаря DDD (Domain Driven Design). В противоположность к Database Driven Design в DDD разработка начинается с проектирования бизнес логики, принимая во внимание только особенности предметной области и игнорируя все, что связано с особенностями базы данных или других способов хранения данных. Способ хранения бизнес объектов реализуется во вторую очередь.

Применение данного паттерна не предполагает создание только одного объекта репозитория во всем приложении. Хорошей практикой считается создание отдельных репозиториев для каждого бизнес-объекта или контекста, например: OrdersRepository, UsersRepository, AdminRepository.

Пример

public interface IPostsRepository
{
    void Save(Post mypost);
    Post Get(int id);
    PaginatedResult<Post> List(int skip,int pageSize);
    PaginatedResult<Post> SearchByTitle(string title,int skip,int pageSize);
}
  • Метод save определяет какой объект передан - новый или существующий, в зависимости от этого использует команду insert или update.
  • Методы List и SearchByTitle возвращают список постов, в котором указывается информация об отобранной странице.

Generic Repository это антипаттерн

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

public interface IRepository<T>
{
   IEnumerable<T> GetAll();
   T GetByID(int id);   
   void Add(T entity);
   void Update(T entity);
   void Delete(T entity);
}

Репозиторий и ORM

Возможно у вас возник вопрос: Зачем использовать репозиторий, если я использую ORM?

Действительно, ORM позволяет:

  • работать с данными, оперируя бизнес-объектами (POCO).
  • легко сменить поставщика данных (SQL Server на MySql и т.д.)

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

  • Если вы хотите сменить поставщика данных на NoSql базу данных или файлы.
  • Вы можете использовать несколько поставщиков данных. Например, MySql для данных и файловую систему для хранения изображений.
  • Строго говоря модель ORM не является моделью предметной области. И в случае когда это действительно разные вещи, а используется только модель ORM, то это уже не DDD.

Если ваш репозиторий возвращает IQueryable, то это ошибка, т.к. репозиторий не должен возвращать что-либо относящееся к способу хранения. Правильно было бы IEnumerable.

Преимущества паттерна Репозиторий

  • Выделение общей логики (DRY): проверки, значения по умолчанию, логирование и т.д..
  • Независимость бизнес-логики от способа хранения. Используя репозиторий, вы обращаетесь с коллекциями бизнес объектов (POCO), но не с database related объектами, не с Data Access Objects. Возможность использовать разные способы хранения: ORM, rdbms, cloud storage, file system etc, заменять их и комбинировать.
  • Работая через интерфейсы вы можете создать несколько реализаций репозитория. Это помогает при тестировании (можно передавать заглушку репозитория при тестировании бизнес-логики) и при изменении способа хранения данных. Конкретная реализация репозитория может регистрироваться в IoC и т.о. ни один модуль приложения за исключением точки входа не будет связан с модулем реализации репозитория. Приложение будет работаеть с интерфейсом репозитория и объектами бизнес-логики.

Репозиторий и DAL (Data Access Layer, Persistence Layer)

Репозиторий - это высокоуровневая абстракция доступа к данным. Интерфейс каждого конкретного репозитория (контракт) определяется в слое бизнес-логики наряду с классами предметной области. Реализация каждого репозитория находится в слое доступа к данным DAL. Соответственно DAL состоит из реализации каждого репозитория, ORM-специфичных классов, сущностей, классов-сопоставлений (mapping), контекстов данных и т.д.

Ссылки

Источник: Серия статей by Mike Mogosanu из его блога https://blog.sapiensworks.com:

На момент написания этой заметки статьи Mike размещались в категории Repository, затем он переделал свой блог и эти статьи попали в категорию Best Practces, что охватывает и другие вопросы, выходящие за рамки темы Паттерн Репозиторий. Поэтому привожу здесь ссылку на версию статей 2014 года.

@pphilipp
Copy link

Приветствую Есенин :), отличное определение, буду благодарен если починишь ссылку "Источник: Серия статей.".

@maestrow
Copy link
Author

@pphilipp Восстановил ссылку :). Хоть и прошло почти 3 года с момента этой просьбы :). На самом деле я не заметил уведомления на этот комментарий, поэтому не ответил. Несмотря на прошедшее время, все же решил найти и восстановить ссылки на исходный материал, т.к. актуальности и ценности он не теряет.

@pphilipp
Copy link

❤️

@Masynchin
Copy link

Работая через интерфейсы вы можете создать несколько реализаций репозитория. Это помогает при тестировании (можно передавать заглушку репозитория при тестировании бизнес-логики)

Разве в таком случае у тестов будет хоть какая-то ценность? Зачем нам тестировать заглушку?

@Skorp567
Copy link

Skorp567 commented Jan 6, 2022

Как создать Рипозиторий ссылку для бот ватсап через термукс?

@dimuska139
Copy link

@Masynchin так речь идёт про тестирование бизнес-логики в unit-тестах. Заглушки репозиториев там нужны, чтобы не было реальных запросов к БД. Т.е. происходит изоляция бизнес-логики в тесте - чтобы тестировать именно её, а не нижележащий слой приложения (репозитории). Если нужно реальные запросы к БД тестировать, то это уже другой вид тестов.

@Yoyo19911
Copy link

Добрый день, есть вопрос:
Если интерфейс репозитория каждой бизнес-сущности свой, то как реализовать тот самый DRY?
Куда и как выносить общую логику?

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment