Паттерны доступа к данным: Repository, Unit of Work, Specification
↑ 8.1 Принципы и паттерны проектирования в C♯ · ← Предыдущая · → Следующая
Зачем это на собесе
Часть вопроса «нужен ли Repository поверх EF» (см. Repository и Unit of Work поверх EF: нужно ли); здесь сами паттерны.
Объяснение
| Паттерн | Суть |
|---|---|
| Repository | коллекция-подобный интерфейс к агрегатам; скрывает хранилище |
| Unit of Work | отслеживает изменения и сохраняет их одной транзакцией |
| Specification | объект-условие запроса, комбинируется (And, Or) |
| Query Object / Read model | отдельные запросы для чтения (CQRS) |
| Data Mapper vs Active Record | отделять ли сущность от хранения |
| Identity Map | один объект на ключ в рамках сессии |
public abstract class Specification<T>
{
public abstract Expression<Func<T, bool>> Criteria { get; }
public Specification<T> And(Specification<T> other) => new AndSpec<T>(this, other);
}
public class PaidOrders : Specification<Order> { public override Expression<Func<Order, bool>> Criteria => o => o.Status == Status.Paid; }
public class OrdersOfCustomer(Guid id) : Specification<Order> { public override Expression<Func<Order, bool>> Criteria => o => o.CustomerId == id; }
public Task<List<Order>> ListAsync(Specification<Order> spec, CancellationToken ct) => db.Orders.Where(spec.Criteria).ToListAsync(ct);Repository агрегата: GetAsync(id), Add(order); Unit of Work — SaveChangesAsync в конце use case. В DDD репозитории — на агрегат, а не на таблицу.
Нюансы и подводные камни
- Generic-репозиторий с
IQueryableпротекает: детали запросов уходят в вызывающий код. - Specification на
Expressionдолжна транслироваться в SQL, иначе клиентское вычисление. - Репозиторий для чтения (списки, отчёты) лучше заменить запросами напрямую к БД.
- Транзакционные границы — на уровне use case.
Практика
- Реализуйте Specification для фильтров заказов и скомбинируйте их.
- Сравните объём кода: DbContext напрямую и репозиторий.
- Сделайте отдельный read-запрос для списка вместо репозитория.
Вопросы с ответами
Что такое Unit of Work?
Паттерн, накапливающий изменения и сохраняющий их атомарно; в EF Core это
DbContext.
Когда Specification оправдана?
При множестве повторно используемых комбинируемых условий выборки.