Прогноз на 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 – RepositoryStorage 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<User> WhereGroupNameIs(this IQueryable<User> 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<D> в Predicate<S> і виконає його на елементах сховища?
Та ні як.
Але рішення дуже просте для кожного 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 ).

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.