Прогноз на Specification pattern в Domain layer — ожидаются проблемы
Data Access Layer — одна из наиболее больных тем.
Написание хорошего слоя доступа к данным — это не тривиальная задача. Примеров реализации невероятно много, но адекватных среди них единицы.
Можно ли считать реализацию шаблона Repository — DAL?
Вот что предлагают MS msdn.microsoft.com/en-us/library/ff649690.aspx
А вот и местные работы habrahabr.ru/post/52173
Варианты довольно нормальные.
Но когда я вижу
«Репозиторий — это фасад для доступа к базе данных.»
…
Я предпочитаю деление Domain Layer — Repository — Storage Layer (всего лишь термины)
Участники слоев
Domain Layer
Aggregation root
— martinfowler.com/bliki/DDD_Aggregate.html (по простому — объект предметной модели о котором знает Repository)
Query
(собственно о нем речь) — обязательно формируется в терминах Предметной области
Repository
Его величество
Repository
— martinfowler.com/eaaCatalog/repository.html
Mapper
— martinfowler.com/eaaCatalog/mapper.html (знает как преобразовать Storage entity в Aggregation root и на оборот)
UoW
— martinfowler.com/eaaCatalog/unitOfWork.html (без этой штуки, нет права на ошибку, а с ней есть)
Storage Layer
Storage entity — об этом коротко не скажешь.
И так DAL это 6 (5 если Mapper считать частью Repository) блоков, каждый из которых играет важную роль.
Запрос — всего лишь параметры фильтрации. В качестве запроса передаваемому в Repository мне очень нравится идея Specification www.codeproject.com/Articles/670115/Specification-pattern-in-Csharp Так же есть habrahabr.ru/post/171559.
Формировать отдельные классы фильтры — работа так себе, но когда начинаешь все это использовать
query = CarSpecifications.ByMark( mark ).
And( CarSpecifications.ByColor( color ).Not() ) ;
cars = carRepository.Get(query);
Понимаешь, что это того стоило. Часто используемые запросы, для удобства, можно определить в CarSpecifications.
Особо понравился комментарий
Напишите x=>x.GroupName == groupName один раз и засуньте его в статический метод-расширения с говорящим названием:
public class UserQueryExtensions { public static IQueryable
WhereGroupNameIs(this IQueryable users, strin name) { return users.Where(u => u.GroupName == name); } } В 10 раз меньше кода, а результат тот же.
С помощью этого ну очень удобно комбинировать запросы Or и Not. Ну и статика противоречит DI.
С точки зрения проектирования шаблон прекрасен. С помощью маленьких объектов, которые просты для понимания (главное для каждого фильтра создать отдельный класс, а не использовать
new ExpressionSpecification (o => o.BrandName == BrandName.Samsung);)
Кресты, это откат к тому от чего Repository должен был избавить.)
можно формировать сложные фильтры.
Фильтры применяются к объектам предметной области (которые являются так же и Aggregation root).
И все бы было прекрасно, если бы не было так ужасно. То из за чего я НЕНАВИЖУ программирование — суровую действительность отраженную в не совершенстве систем. Specification является предикатом истинность которого определяется исходя из объекта предметной области,
увы и ах, как Repository переведет ISpecification в Predicate и выполнит его на элементах хранилища?
Да ни как.
Но решение очень простое для каждого S выполнить S → D (маппинг) и уже к D применить спецификацию. И вроде как опять все хорошо, но суровая действительность не отступает, диктуя все новые преграды.
Постановка квеста: есть бд, в ней живет таблица, в таблице 50к записей.
Применяем алгоритм для каждого S, GetAll… вы не ошиблись OutOfMemory.
Подполз муравей к жд путям, и решил «умный в гору не пойдет, умный гору обойдет, я ж не дурак».
В общем муравья так до сих пор и не нашли. Идем по методу муравья, боремся с суровой действительностью. Получаем по 1 объекту
1 S, S → D, IsSatisfiedBy (D)
2 S, S → D, IsSatisfiedBy (D)
3 S, S → D, IsSatisfiedBy (D)
K
50000 S, S → D, IsSatisfiedBy (D)
Поздравляю мы выполнили
50к запросов к бд + 50к применили функцию преобразования + 50к * n спецификаций в переданной цепочке, и все это = БЕСКОНЕЧНОСТИ
(клиент ответа не дождется, в лучшем случае умрет от старости, в худшем уйдет к конкурентам) и все это ради пары объектов удовлетворяющим критерию поиска (за то работает, если конечно SQL не умрет от Dos покушения).
Это конец?
Конечно нет, прокачиваем алгоритм запросы к бд делаем порциями, запрашиваем по m элементов в каждом запросе, теперь мы выполнили
50к / m запросов к бд + 50к применили функцию преобразования + 50к * n спецификаций в переданной цепочке, что так же = БЕСКОНЕЧНОСТИ.
Я иссяк, я пересох, я сдался.
На этом суровая реальность в лице времени и несовершенства систем все ж одолела. Яркий пример когда технологии сдерживают отличное проектирование… На фото выглядит замечательно, в реале не особо (это мягко сказано).
P.S.
Спецификации можно применять на небольших наборах данных, например если известно, что объектов будет не много и GetAll S, S → D выполняется за приемлемое время, а так же если есть возможность сделать полный кэш данных в память и дальше просто применять IsSatisfiedBy (D).