Programming
.NET
Designing and refactoring
Comments 24
0
То, что в примерах называется RavenSession — по сути репозиторий.

Интересно, что будет при таком подходе, когда вместо того, чтобы поменять в одном месте реализацию, например, FindFree, придется менять LINQ запрос во всех местах где он используется.

Ну и про unit of work полезно помнить тоже.
0
Ну так автор вроде как и предлагает не сооружать вокруг него еще один слой репозитория.

По второму — я такие случаи решаю выносом LINQ выражений в extension methods к IQueryable, или ещe какие-нибудь объекты-запросы. Но тоже не буду пытаться абстрагироваться от ORM в большинстве случаев. Хотя бывают датацетричные приложения, которые этого требуют.
0
«Не сооружать репозиторий» приводит к обозначенным проблемам.

Чаще всего написать экстеншен метод вы решите после пары итераций рефакторинга уже написанного кода.

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

0
К каким обозначенным? Если изменить FindFree — то он у меня уже есть в моих extension методах- я его в одном месте меняю. Ну только в этих extension методах я не пытаюсь создать обертки вокруг SingleOrDefault и других тривиальных случаев, чем придется заниматься репозиторию
0
И более того — такие IQueryable estensions позволяют объединять их в цепочки:
например, var userActivePosts = posts.IsActive().ForUser(user)
0
Интересная мысль.

По-моему, подобный подход может привести к дубляжу кода запросов. Хорошо, когда запрос — это просто «загрузи все посты». А представьте, если посты могут быть помеченными как «archived» с помощью флага. Пользователь же всегда должен работать с активными постами, и есть редкие случаи, когда ему показываются и архивные посты тоже.

В таком случае надо будет искать все Query к постам и добавлять везде, где надо, .IsNotArchived(), нежели просто поменять методы в репозитории Post. Имхо, это уже дубляж пойдет.
0
Ну так автор и не предлагает оставить дублицированный код как есть. Просто вместо создания слоя репозитария вокруг ORM (готового репозитария) — использовать более тонкие концепции — вынося этот код в них.

Как я написал выше — для меня так работают доп классы с extension методами к iqueryable, или классы генерирующие деревья запросов.
0
Единственный минус такого подхода, если вы захотите заменить тот же RavenDB на Redis или SqlServer, при этом выбор механизма хранения будет зависеть от конкретного клиента.
+1
Это в сферическом вакууме можно разработать систему, которая легко переживёт смену провайдера данных. В реальных системах сколько бы абстракций вы не нагородили при смене провайдера придётся много чего допиливать, если, конечно, у вас система не построена только на простейших запросах, которые можно через LINQ все написать (eager fetch, кэш запросов, разные реализации постраничных результатов, batching запросов, query plans hints и т.п.).

Да и сколько раз в жизни вы меняли провайдер данных у работающей системы? :)
0
Текущий проект уже работает с 7 разными провайдерами =) Но у меня специфическая тема. В основном LDAP или LDAP over SQL и тд. Пока еще дошли руки попробовать свои LINQ провайдеры к ним писать =)
0
Ну, в специфичных проектах — не спорю. Но я думаю, что подавляющее большинство обычных проектов никогда не меняли и не будут менять провайдера.
+1
Еще зависит от сложности системы и уровней абстракции, так как например возможны репозитории для UI, которые будут инкапсулировать логику создания моделей и тд. А так же возможны ситуации, когда часть логики системы может быть InProc так и Remote, в зависимости от нагрузки и сценариев развертывания.

Меня лично очень напрягли толстые контроллеры в посте, но это ИМХО. Возможно в небольшом проекте это будет ок, но при увеличении сложности, может встать колом, допустим, при аудите изменений объектов и логировании.
0
Да, про это и речь — зачастую делают репозиторий поверх ORM репозиториев без реальной необходимости. Абстракции «на будущее» это такое же зло как и преждевременная оптимизация.
0
Более того использование более одного провайдера, это гарантированная деградация производительности и/или удобства. В итоге вместо использования фич. Oracle, приложение ограничено функционалом MySQL. Хотя вся контора давно и плотно сидит на Oracle и имеет кучу ДБА высокой квалификации.

+1
public interface IConferenceRepository
{
    IEnumerable<Conference> FindAll();
    IEnumerable<Conference> FindFuture();
    IEnumerable<Conference> FindFree();
    IEnumerable<Conference> FindPaid();
}


На мой взгляд, данный код в любом случае придется писать в случае клиент-серверного программирования. Вы же не позволите удаленному клиенту вручную управлять базами данных. А вот максимально удобно реализовать указанный выше интерфейс сейчас можно с помощью ASP.NET Web API, ну или WCF(если не ошибаюсь)

Как понимаю, нет смысла писать абстракцию для методов, реализованных в указанном интерфейсе, и именно в коде контроллера размещать запросы Linq.
+1
Есть ещё другой способ — он чуть более «многословный», но вполне элегантный — перевод всех операций вашего домена в форму Command/Query классов.

Проблема больших репозиториев ещё и в том, что они подрывают всю идею dependency injection — скажем, если у вас сервис зависит от какого-нибудь репозитория с кучей методов, то вы никогда не поймёте а какие же именно методы использует сервис пока не посмотрите его код. Соответственно, трудно изолировать injected функционал.

Я уже не говорю о том, что подобные репозитории нарушают SRP.

Вообще, конечно, очень много систем написано подобным способом и они отлично работают. Но в данном случае мы говорим о «полировании» архитектуры системы под конкретные нужды.
0
По-моему, как раз из-за желания соблюдать SRP и возник паттерн «Репозиторий». Перенося его функциональность (создание запросов) в модель или контроллер мы добавляем им ещё одну ответственность.

А проблема больших репозиториев достаточно легко, имхо, решается декомпозицией, возможно наследованием и параметризацией. Зачастую в репозитории, скажем в UserRepository, создаются методы практически на автомате для каждого запроса, возвращающего объекты User, отличающегося от другого одним-двумя параметрами запроса, например проверкой поля active или archive. То есть получаются репозитории с набором методов, почти дублирующих функциональность друг друга, типа findAll() и findActive(), findById() и findActiveByID и т. п., а ведь можно было бы сделать два репозитория AllUserRepository и ActiveUserRepository и в пару раз сократить количество методов в каждом — общее одинаково, но разделены между двумя классами.
0
Репозитории которые есть у большинства проектов это в лучшем случае свалка запросов вида от QueryActiveUsers до QueryUserIdsFromRegionByName. Это далеко от SRP в его чистом виде. Конечно, всегда можно сказать, что, мол, repository выполняет одну задачу «выборка информации» и что это SRP — но тогда можно сказать про любой код «он выполняет действия» и это тоже будет SRP.

В общем, если уж заморачиваться с этим делом нормально, то нужно каждый метод расписывать Role Interface-ом и в конечном итоге придём к Commands/Queries классам (к слову, очень достойная архитектура).

Другое дело, что это не всегда оправданно. Вот тут уже нужно задать себе вопрос — а если это не оправданно, то зачем вообще надстраивали свой репозиторий над ORM-ским репозиторием?
0
Так репозиторий не просто выполняет действия, а вполне определенные — выборку информации определенного типа из хранилища.

Репозитории, емнип, и рекомендуются в случае сложных систем, для приложений сводящихся к CRUD они действительно избыточны. Субъективно оцениваю так: появляется (или точно знаю, что появится) дублирование кода запросов — пора делать репозиторий. Правда ещё один нюанс возникает, требующий принятия решений — если сделал репозиторий для одного типа объектов домена, то надо ли делать для остальных ради однообразия архитектуры.
0
Ещё нужно не забывать про то, что нам уже зачастую ORM даёт готовый репозиторий и нужно ещё обосновать необходимость новой абстракции поверх этого. Дублирование кода запросов достаточно просто решить и без репозиториев (к примеру, спецификациями или, на крайний случай, методами расширения как предлагал Ayende).
0
Многое зависит от ORM и от языка. Согласен с
Абстракции «на будущее» это такое же зло как и преждевременная оптимизация.
, но с небольшим уточнением — если точно знаешь, абстракция понадобится завтра, через неделю или через год, то лучше сделать её сейчас. А вот оптимизация может подождать, даже если точно знаешь, что она понадобится. Но вот абстракцию под неё лучше заранее создать :)
0
Ну, так же можно говорить про оптимизацию, мол, закладывать её нужно на будущее, а то ведь никак.
Абстракция может также подождать до нужного момента — потом рефакторинг.
0
Архитектура не более. Если пойти по минимальному пути и добавлять «хотелки» уже после, вы добавите себе работы в разы, или даже на порядки.

Чем кстати и плох agile, вроде идея нормальная, но требует крутого спеца, который держит все в голове, и выкладывает по мере необходимости.
-1
Существенные минусы, на мой взгляд: «тяжеловесные» контроллеры, увеличенная связанность, необходимость тестирования логики выборки данных вместе с логикой обработки данных. По моему, этого достаточно, чтобы потратить время на инкапсуляцию.
Only those users with full accounts are able to leave comments. , please.