Строго говоря, репозиторий предлагает семантику коллекции для получения / размещения объектов домена. Он обеспечивает абстракцию вокруг вашей реализации материализации (ORM, свернутая вручную, фиктивная), так что потребители доменных объектов отделены от этих деталей. На практике репозиторий обычно абстрагирует доступ к сущностям, то есть объектам домена с идентичностью и, как правило, постоянным жизненным циклом (в варианте DDD репозиторий предоставляет доступ к агрегированным корням).
Минимальный интерфейс для хранилища следующий:
void Add(T entity);
void Remove(T entity);
T GetById(object id);
IEnumerable<T> Find(Specification spec);
Несмотря на то, что вы увидите различия в именах и добавление семантики Save / SaveOrUpdate, вышеприведенная идея является «чистой» идеей. Вы получаете ICollection Add / Remove members плюс некоторые искатели. Если вы не используете IQueryable, вы также увидите методы поиска в репозитории, такие как:
FindCustomersHavingOrders();
FindCustomersHavingPremiumStatus();
В этом контексте есть две проблемы, связанные с использованием IQueryable. Во-первых, это потенциальная возможность утечки деталей реализации клиенту в форме отношений объекта домена, то есть нарушения Закона Деметры. Во-вторых, хранилище приобретает обязанности по поиску, которые могут не принадлежать собственно хранилищу объекта домена, например, находить проекции, которые меньше относятся к запрашиваемому объекту домена, чем к связанным данным.
Кроме того, использование IQueryable «ломает» шаблон: репозиторий с IQueryable может предоставлять или не предоставлять доступ к «объектам домена». IQueryable предоставляет клиенту множество опций о том, что будет реализовано, когда запрос будет наконец выполнен. Это основной смысл дискуссии об использовании IQueryable.
Что касается скалярных значений, вам не следует использовать репозиторий для возврата скалярных значений. Если вам нужен скаляр, вы, как правило, получите это от самой сущности. Если это звучит неэффективно, это так, но вы можете не заметить, в зависимости от ваших характеристик нагрузки / требований В случаях, когда вам нужны альтернативные представления объекта домена, из-за соображений производительности или из-за необходимости объединения данных из множества объектов домена, у вас есть два варианта.
1) Используйте репозиторий сущностей, чтобы найти указанные сущности, и проект / карту в плоском виде.
2) Создайте интерфейс поиска, предназначенный для возврата нового типа домена, который инкапсулирует нужное вам плоское представление. Это не был бы репозиторий, потому что не было бы семантики коллекции, но он мог бы использовать существующие репозитории под обложками.
Если вы используете «чистый» репозиторий для доступа к постоянным сущностям, следует учитывать одну вещь: вы подвергаете риску некоторые из преимуществ ORM. В «чистой» реализации клиент не может предоставить контекст для того, как будет использоваться объект домена, поэтому вы не можете сообщить хранилищу: «эй, я просто собираюсь изменить свойство customer.Name, так что не беспокойтесь о тех загруженных ссылках. С другой стороны, вопрос заключается в том, должен ли клиент знать об этом материале. Это обоюдоострый меч.
Что касается использования IQueryable, большинству людей, кажется, удобно «нарушать» шаблон, чтобы получить преимущества динамической композиции запросов, особенно для таких обязанностей клиента, как разбиение на страницы / сортировка. В этом случае вы можете иметь:
Add(T entity);
Remove(T entity);
T GetById(object id);
IQueryable<T> Find();
и вы можете покончить со всеми этими пользовательскими методами Finder, которые действительно загромождают репозиторий по мере роста требований к запросу.