Repository (Репозиторий)
Посредничает между уровнями области определения и распределения данных (domain and data mapping layers), используя интерфейс, схожий с коллекциями для доступа к объектам области определения.
Система со сложной моделью области определения может быть упрощена при помощи дополнительного уровня, например Data Mapper, который бы изолировал объекты от кода доступа к БД. В таких системах может быть полезным добавление ещё одного слоя абстракции поверх слоя распределения данных (Data Mapper), в котором бы был собран код создания запросов. Это становится ещё более важным, когда в области определения множество классов или при сложных, тяжелых запросах. В таких случаях добавление этого уровня особенно помогает сократить дублирование кода запросов.
Паттерн Repository посредничает между слоем области определения и слоем распределения данных, работая, как обычная колекция объектов области определения. Объекты-клиенты создают описание запроса декларативно и направляют их к объекту-репозиторию ( Repository ) для обработки. Объекты могут быть добавлены или удалены из репозитория, как будто они формируют простую коллекцию объектов. А код распределения данных, скрытый в объекте Repository , позаботится о соответсвующих операциях в незаметно для разработчика. В двух словах, паттерн Repository инкапсулирует объекты, представленыые в хранилище данных и операции, производимые над ними, предоставляя более объектно-ориентированное представление реальных данных. Repository также преследует цель достижения полного разделения и односторонней зависимости между уровнями области определения и распределения данных.
Использована иллюстрация с сайта Мартина Фаулера.
- Главная
- Список паттернов
- Сайт создан и поддерживается Василием Кулаковым.
Репозиторий — что это такое? Определение, значение, перевод
Репозиторий (ударение на вторую «о») это электронное хранилище данных, причём как правило речь идёт о программном коде. У репозиториев две основные функции, каждую из которых мы сейчас разжуём помягче.
Во-первых, репозитории служат для распространения полезных программ. За долгие десятилетия существования компьютеров человеки написали миллионы строчек полезного кода, которым можно воспользоваться, чтобы не писать его самому. В репозиториях хранятся стабильно работающие и проверенные версии программ, которые можно либо скачать бесплатно, без регистрации и СМС, либо таки заплатить, хотя такое встречается реже. Можно сравнить публичные репозитории с такими же библиотеками, куда каждый желающий может зайти и взять себе крупицу знаний.
Во-вторых, программисты используют репозитории для контроля версий, который особенно важен при разработке одной и той же программы несколькими программистами. В репозиториях хранятся стабильные версии программы, причём каждое изменение должно быть задокументировано: кто, когда и зачем его сделал. Такой подход к программированию позволяет выявлять накосячивших программистов и откатывать программы к более ранней версии.
Понравилась страница?
Пожалуйста, поделитесь ссылкой с друзьями:
Паттерн ‘Репозиторий’ в ASP.NET
Одним из наиболее часто используемых паттернов при работе с данными является паттерн ‘Репозиторий’. Репозиторий позволяет абстрагироваться от конкретных подключений к источникам данных, с которыми работает программа, и является промежуточным звеном между классами, непосредственно взаимодействующими с данными, и остальной программой.
Допустим, у нас есть одно подключение к базе данных MS SQL Server. Однако, что если в какой-то момент времени мы захотим сменить подключение с MS SQL на другое — например, к бд MySQL или MongoDB. При стандартном подходе даже в небольшом приложении, осуществляющем выборку, добавление, изменение и удаление данных, нам бы пришлось сделать большое количество изменений. Либо в процессе работы программы в зависимости от разных условий мы хотим использовать два разных подключения. Таким образом, репозиторий добавляет программе гибкость при работе с разными типами подключений.
Например, у нас в программе есть следующий класс модели, с которой мы работаем:
public class Book < public int Id < get; set; >public string Name < get; set; >public string Author < get; set; >public int Price < get; set; >>
И также имеется следующий класс контекста данных:
public class BookContext : DbContext < public BookContext() : base("DefaultConnection") < >public DbSet Books < get; set; >>
Пусть в файле web.config у меня определено два подключения:
Первое подключение используется контекстом данных для работы с бд MS SQL Server. Второе подключение — подключение к БД MongoDB.
Теперь определим интерфейс репозитория:
interface IRepository : IDisposable where T : class < IEnumerableGetBookList(); // получение всех объектов T GetBook(int id); // получение одного объекта по id void Create(T item); // создание объекта void Update(T item); // обновление объекта void Delete(int id); // удаление объекта по id void Save(); // сохранение изменений >
В данном случае мы создали обобщенный интерфейс, так как он более гибкий, по сравнению с обычной. Хотя так как у нас работа идет только с классом Book, можно было бы сделать и необобщенный вариант:
interface IRepository : IDisposable < IEnumerableGetBookList(); Book GetBook(int id); void Create(Book item); void Update(Book item); void Delete(int id); void Save(); >
На случай, если контекст данных будет подразумевать освобождение или закрытие подключений, интерфейс репозитория применяет интерфейс IDisposable.
Теперь создадим непосредственную реализацию для работы с MS SQL Server:
public class SQLBookRepository : IRepository < private BookContext db; public SQLBookRepository() < this.db = new BookContext(); >public IEnumerable GetBookList() < return db.Books; >public Book GetBook(int id) < return db.Books.Find(id); >public void Create(Book book) < db.Books.Add(book); >public void Update(Book book) < db.Entry(book).State = EntityState.Modified; >public void Delete(int id) < Book book = db.Books.Find(id); if(book!=null) db.Books.Remove(book); >public void Save() < db.SaveChanges(); >private bool disposed = false; public virtual void Dispose(bool disposing) < if(!this.disposed) < if(disposing) < db.Dispose(); >> this.disposed = true; > public void Dispose() < Dispose(true); GC.SuppressFinalize(this); >>
Почти все методы SQLBookRepository работают с контекстом данных, который создается в конструкторе класса.
Теперь применим репозиторий в контроллере:
public class HomeController : Controller < IRepositorydb; public HomeController() < db = new SQLBookRepository(); >public ActionResult Index() < return View(db.GetBookList()); >public ActionResult Create() < return View(); >[HttpPost] public ActionResult Create(Book book) < if(ModelState.IsValid) < db.Create(book); db.Save(); return RedirectToAction("Index"); >return View(book); > public ActionResult Edit(int id) < Book book = db.GetBook(id); return View(book); >[HttpPost] public ActionResult Edit(Book book) < if (ModelState.IsValid) < db.Update(book); db.Save(); return RedirectToAction("Index"); >return View(book); > [HttpGet] public ActionResult Delete(int id) < Book b = db.GetBook(id); return View(b); >[HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) < db.Delete(id); return RedirectToAction("Index"); >protected override void Dispose(bool disposing) < db.Dispose(); base.Dispose(disposing); >>
В данном случае в всех методах контроллера мы работаем не с методами конкретного класса, а с методами интерфейса IRepository. И только в конструкторе контроллера мы определяем непосредственный тип репозитория: db = new SQLBookRepository(); . Таким образом, мы практически избавляемся от зависимости к определенному типу подключения.
Но у меня в файле web.config определено еще подключение к MongoDB. И теперь создадим репозиторий, который будет использовать это подключение:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using MongoDB.Bson; using MongoDB.Driver; using MongoDB.Driver.Builders; using System.Configuration; public class MongoBookRepository : IRepository < MongoClient client; MongoServer server; MongoDatabase database; public MongoBookRepository() < var con = new MongoConnectionStringBuilder( ConfigurationManager.ConnectionStrings["MongoDb"].ConnectionString); client = new MongoClient(con.ConnectionString); server = client.GetServer(); database = server.GetDatabase(con.DatabaseName); >public MongoCollection Books < get < return database.GetCollection("books"); > > public IEnumerable GetBookList() < return Books.FindAll(); >public Book GetBook(int id) < return Books.FindOneById(id); >public void Create(Book book) < try < int if (Books.Count() >0) => x.Id); book.Id = ++id; Books.Insert(book); > catch < >> public void Update(Book book) < try < Books.Save(book); >catch < >> public void Delete(int id) < try < var query = Query.EQ(e => e.Id, id); if (query != null) < Books.Remove(query); >> catch < >> public void Save() <> public void Dispose() <> >
Для работы с MongoDB используется другой API, однако, так как класс MongoBookRepository применяет тот же интерфейс репозитория, то общий набор методов у этого класса будет то же, что и у SqlBookRepository. Поэтому при необходимости можно просто заменить в контроллере тип репозитария:
public HomeController()
А весь остальной код будет тем же, что и раньше. Таким образом, мы можем уйти от явной привязки к одному хранилищу данных и тем самым повысить гибкость приложения.
Паттерн «Репозиторий»
Репозиторий один из самых популярных паттернов для доступа к данным. Он используется для абстрагирования от конкретной реализации и нюансов работы с источником данных, внешних сервисов, файловой системой и т. д.
С одной стороны, это очень простой паттерн, который позволяет скрыть сложность работы с БД. Но с другой стороны, спросите 10 программистов описать этот шаблон и вы получите 10 разных реализаций.
Самая большая проблема этого шаблона — множество вопросов, которые возникают при его реализации и дальнейшей поддержки.
Изначально хотел сам расписать проблемы, но за меня это уже сделали, поэтому настоятельно советую прочитать статью: «Проблемы паттерна Репозиторий».
- Что делать если репозиториям нужно использовать закрытые методы друг друга?
- Можно ли использовать один репозиторий на весь проект или делать репозиторий на каждую сущность?
- Нужно ли дублировать методы репозитория в сервис или мы можем напрямую использовать репозиторий в контроллерах?
- Нужно ли возвращать IQueryable и как это повлияет на дизайн системы в случае с .NET кодом? Если нет, то как правильно изменять сущности без использования Change Tracking?
- Как правильно объединить репозиторий и UoW?
Отдельно также хочу отметить доклад: Денис Цветцих «Repository и UnitOfWork в 2020 году, must have или антипаттерн?». В нем также поднимаются проблемы репозитория, способы их решения и варианты замены этот шаблона на другой.
Репозиторий — хороший паттерн, который должен быть в арсенале любого программиста, но стоит использовать его с умом и учитывать проблемы, которые он может вызвать.
Для себя я решил что репозиторий хорошо подходит, когда вся логика приложения вписывается в CRUD модель. Но если логика более сложная или приложение подразумевает Task Base UI, тогда лучше прибегнуть к подходу CQRS. Он позволяет разбить сложную бизнес логику включая репозитории на независимые объекты, каждый из которых выполняет только одну бизнес задачу или use case.
Я кроме все прочего не люблю репозитории за:
- Ограничение функциональности ORM, большенство специфических операций недоступны. В зависимости от реализации можем потерять Change Tracking.
- Дополнительный мапинг из доменных объектов в DTO.
- Дополнительный слой абстракции, который зачастую просто не нужен. Особенно если речь идет о небольших сервисах где логику нужно делать максимально простой и незамысловатой.
- Работает только в простых CRUD сценариях.