Прогноз на Specification pattern в Domain layer — ожидаются проблемы

Data Access Layer — одна из наиболее больных тем.
Написание хорошего слоя доступа к данным — это не тривиальная задача. Примеров реализации невероятно много, но адекватных среди них единицы.
Можно ли считать реализацию шаблона Repository — DAL?
Вот что предлагают MS msdn.microsoft.com/en-us/library/ff649690.aspx
image
А вот и местные работы 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).

© Habrahabr.ru