Комментарии 24
То, что в примерах называется RavenSession — по сути репозиторий.
Интересно, что будет при таком подходе, когда вместо того, чтобы поменять в одном месте реализацию, например, FindFree, придется менять LINQ запрос во всех местах где он используется.
Ну и про unit of work полезно помнить тоже.
Интересно, что будет при таком подходе, когда вместо того, чтобы поменять в одном месте реализацию, например, FindFree, придется менять LINQ запрос во всех местах где он используется.
Ну и про unit of work полезно помнить тоже.
0
Ну так автор вроде как и предлагает не сооружать вокруг него еще один слой репозитория.
По второму — я такие случаи решаю выносом LINQ выражений в extension methods к IQueryable, или ещe какие-нибудь объекты-запросы. Но тоже не буду пытаться абстрагироваться от ORM в большинстве случаев. Хотя бывают датацетричные приложения, которые этого требуют.
По второму — я такие случаи решаю выносом 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. Имхо, это уже дубляж пойдет.
По-моему, подобный подход может привести к дубляжу кода запросов. Хорошо, когда запрос — это просто «загрузи все посты». А представьте, если посты могут быть помеченными как «archived» с помощью флага. Пользователь же всегда должен работать с активными постами, и есть редкие случаи, когда ему показываются и архивные посты тоже.
В таком случае надо будет искать все Query к постам и добавлять везде, где надо, .IsNotArchived(), нежели просто поменять методы в репозитории Post. Имхо, это уже дубляж пойдет.
0
Ну так автор и не предлагает оставить дублицированный код как есть. Просто вместо создания слоя репозитария вокруг ORM (готового репозитария) — использовать более тонкие концепции — вынося этот код в них.
Как я написал выше — для меня так работают доп классы с extension методами к iqueryable, или классы генерирующие деревья запросов.
Как я написал выше — для меня так работают доп классы с extension методами к iqueryable, или классы генерирующие деревья запросов.
0
Единственный минус такого подхода, если вы захотите заменить тот же RavenDB на Redis или SqlServer, при этом выбор механизма хранения будет зависеть от конкретного клиента.
0
Это в сферическом вакууме можно разработать систему, которая легко переживёт смену провайдера данных. В реальных системах сколько бы абстракций вы не нагородили при смене провайдера придётся много чего допиливать, если, конечно, у вас система не построена только на простейших запросах, которые можно через LINQ все написать (eager fetch, кэш запросов, разные реализации постраничных результатов, batching запросов, query plans hints и т.п.).
Да и сколько раз в жизни вы меняли провайдер данных у работающей системы? :)
Да и сколько раз в жизни вы меняли провайдер данных у работающей системы? :)
+1
Текущий проект уже работает с 7 разными провайдерами =) Но у меня специфическая тема. В основном LDAP или LDAP over SQL и тд. Пока еще дошли руки попробовать свои LINQ провайдеры к ним писать =)
0
Ну, в специфичных проектах — не спорю. Но я думаю, что подавляющее большинство обычных проектов никогда не меняли и не будут менять провайдера.
0
Еще зависит от сложности системы и уровней абстракции, так как например возможны репозитории для UI, которые будут инкапсулировать логику создания моделей и тд. А так же возможны ситуации, когда часть логики системы может быть InProc так и Remote, в зависимости от нагрузки и сценариев развертывания.
Меня лично очень напрягли толстые контроллеры в посте, но это ИМХО. Возможно в небольшом проекте это будет ок, но при увеличении сложности, может встать колом, допустим, при аудите изменений объектов и логировании.
Меня лично очень напрягли толстые контроллеры в посте, но это ИМХО. Возможно в небольшом проекте это будет ок, но при увеличении сложности, может встать колом, допустим, при аудите изменений объектов и логировании.
+1
Более того использование более одного провайдера, это гарантированная деградация производительности и/или удобства. В итоге вместо использования фич. Oracle, приложение ограничено функционалом MySQL. Хотя вся контора давно и плотно сидит на Oracle и имеет кучу ДБА высокой квалификации.
0
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.
Вообще, конечно, очень много систем написано подобным способом и они отлично работают. Но в данном случае мы говорим о «полировании» архитектуры системы под конкретные нужды.
Проблема больших репозиториев ещё и в том, что они подрывают всю идею dependency injection — скажем, если у вас сервис зависит от какого-нибудь репозитория с кучей методов, то вы никогда не поймёте а какие же именно методы использует сервис пока не посмотрите его код. Соответственно, трудно изолировать injected функционал.
Я уже не говорю о том, что подобные репозитории нарушают SRP.
Вообще, конечно, очень много систем написано подобным способом и они отлично работают. Но в данном случае мы говорим о «полировании» архитектуры системы под конкретные нужды.
+1
По-моему, как раз из-за желания соблюдать SRP и возник паттерн «Репозиторий». Перенося его функциональность (создание запросов) в модель или контроллер мы добавляем им ещё одну ответственность.
А проблема больших репозиториев достаточно легко, имхо, решается декомпозицией, возможно наследованием и параметризацией. Зачастую в репозитории, скажем в UserRepository, создаются методы практически на автомате для каждого запроса, возвращающего объекты User, отличающегося от другого одним-двумя параметрами запроса, например проверкой поля active или archive. То есть получаются репозитории с набором методов, почти дублирующих функциональность друг друга, типа findAll() и findActive(), findById() и findActiveByID и т. п., а ведь можно было бы сделать два репозитория AllUserRepository и ActiveUserRepository и в пару раз сократить количество методов в каждом — общее одинаково, но разделены между двумя классами.
А проблема больших репозиториев достаточно легко, имхо, решается декомпозицией, возможно наследованием и параметризацией. Зачастую в репозитории, скажем в UserRepository, создаются методы практически на автомате для каждого запроса, возвращающего объекты User, отличающегося от другого одним-двумя параметрами запроса, например проверкой поля active или archive. То есть получаются репозитории с набором методов, почти дублирующих функциональность друг друга, типа findAll() и findActive(), findById() и findActiveByID и т. п., а ведь можно было бы сделать два репозитория AllUserRepository и ActiveUserRepository и в пару раз сократить количество методов в каждом — общее одинаково, но разделены между двумя классами.
0
Репозитории которые есть у большинства проектов это в лучшем случае свалка запросов вида от QueryActiveUsers до QueryUserIdsFromRegionByName. Это далеко от SRP в его чистом виде. Конечно, всегда можно сказать, что, мол, repository выполняет одну задачу «выборка информации» и что это SRP — но тогда можно сказать про любой код «он выполняет действия» и это тоже будет SRP.
В общем, если уж заморачиваться с этим делом нормально, то нужно каждый метод расписывать Role Interface-ом и в конечном итоге придём к Commands/Queries классам (к слову, очень достойная архитектура).
Другое дело, что это не всегда оправданно. Вот тут уже нужно задать себе вопрос — а если это не оправданно, то зачем вообще надстраивали свой репозиторий над ORM-ским репозиторием?
В общем, если уж заморачиваться с этим делом нормально, то нужно каждый метод расписывать Role Interface-ом и в конечном итоге придём к Commands/Queries классам (к слову, очень достойная архитектура).
Другое дело, что это не всегда оправданно. Вот тут уже нужно задать себе вопрос — а если это не оправданно, то зачем вообще надстраивали свой репозиторий над ORM-ским репозиторием?
0
Так репозиторий не просто выполняет действия, а вполне определенные — выборку информации определенного типа из хранилища.
Репозитории, емнип, и рекомендуются в случае сложных систем, для приложений сводящихся к CRUD они действительно избыточны. Субъективно оцениваю так: появляется (или точно знаю, что появится) дублирование кода запросов — пора делать репозиторий. Правда ещё один нюанс возникает, требующий принятия решений — если сделал репозиторий для одного типа объектов домена, то надо ли делать для остальных ради однообразия архитектуры.
Репозитории, емнип, и рекомендуются в случае сложных систем, для приложений сводящихся к CRUD они действительно избыточны. Субъективно оцениваю так: появляется (или точно знаю, что появится) дублирование кода запросов — пора делать репозиторий. Правда ещё один нюанс возникает, требующий принятия решений — если сделал репозиторий для одного типа объектов домена, то надо ли делать для остальных ради однообразия архитектуры.
0
Ещё нужно не забывать про то, что нам уже зачастую ORM даёт готовый репозиторий и нужно ещё обосновать необходимость новой абстракции поверх этого. Дублирование кода запросов достаточно просто решить и без репозиториев (к примеру, спецификациями или, на крайний случай, методами расширения как предлагал Ayende).
0
Многое зависит от ORM и от языка. Согласен с
Абстракции «на будущее» это такое же зло как и преждевременная оптимизация., но с небольшим уточнением — если точно знаешь, абстракция понадобится завтра, через неделю или через год, то лучше сделать её сейчас. А вот оптимизация может подождать, даже если точно знаешь, что она понадобится. Но вот абстракцию под неё лучше заранее создать :)
0
Ну, так же можно говорить про оптимизацию, мол, закладывать её нужно на будущее, а то ведь никак.
Абстракция может также подождать до нужного момента — потом рефакторинг.
Абстракция может также подождать до нужного момента — потом рефакторинг.
0
Архитектура не более. Если пойти по минимальному пути и добавлять «хотелки» уже после, вы добавите себе работы в разы, или даже на порядки.
Чем кстати и плох agile, вроде идея нормальная, но требует крутого спеца, который держит все в голове, и выкладывает по мере необходимости.
Чем кстати и плох agile, вроде идея нормальная, но требует крутого спеца, который держит все в голове, и выкладывает по мере необходимости.
0
Существенные минусы, на мой взгляд: «тяжеловесные» контроллеры, увеличенная связанность, необходимость тестирования логики выборки данных вместе с логикой обработки данных. По моему, этого достаточно, чтобы потратить время на инкапсуляцию.
-1
Зарегистрируйтесь на Хабре , чтобы оставить комментарий
Ограничивая абстракции (.NET, ASP.NET MVC)