Создаем IT-продукты на заказ

За 5 лет мы создали продукты с топовыми e-commerce компаниями, ритейлом, банками и другими бизнесами по всему миру.

Специализируемся на SaaS-решениях на архитектуре микросервисов, делаем аналитику IT-проекта перед стартом.

Посмотрите, что говорят о нас клиенты и как комфортно мы стартуем проекты.

Для обсуждения проекта пишите на ceo@byndyusoft.com или звоните +7 (904) 305 5263

понедельник, 7 мая 2012 г.

Заменяем QueryFactory на бестелесный IQueryFactory

В статье Проблемный шаблон Repository и судя по комментариям многим не понравилась та часть, где объекты *Query скрываются за IQueryFactory. С первого взгляда кажется, что QueryFactory превращается в очередной god-object.

Я уже отвечал, что это не так, потому что в самой QueryFactory нет логики и она только создает объекты запросы. Теперь я могу дать ссылки на две статьи, где от QueryFactory остался только интерфейс, который реализуется с помощью IoC.

Первая статья моего коллеги Тимура Рахматиллаева совсем недавно выложена на Хабре Получение экземпляра класса запроса по сигнатуре его интерфейса.

Вторая статья моего бывшего коллеги Дмитрия Крючкова была выложена в его блоге Alternative to repository pattern: query objects using Castle Windsor с примеры кода на GitHub'е

Советую изучить обе статьи, довольно интересные подходы. С первого взгляда могут показаться слишком сложными и абстрактными, но если их попробовать, то преимущества становятся очевидны.

24 комментария:

  1. Преимущества всегда очевидны. Дьявол прячется в недостатках.

    Александр, можно вопрос именно к тебе, как к пустившему волну? Ты же придерживаешься принципа SRP при проектировании? Можешь сформулировать responsibility класса LoginCriterion в примере Тимура?

    ОтветитьУдалить
  2. А мне вообще непонятен ажиотаж вокруг этого ServiceLocator-подобного IQueryFactory. Насколько я понимаю, вещь сугубо опциональная. Предпочитаешь явно объявлять зависимости - прокидывай Query напрямую, не любишь прокидывать много зависимостей - прокидывай IQueryFactory, хотя это 1. практически прокидывание IoC контейнера 2. сделает стаббинг/мокинг зависимостей менее интуитивным

    В любом случае, IQueryFactory никак не влияет на достоинства/недостатки Query - он является надстройкой (пусть, на мой взгляд, и сомнительной) над ним.

    ОтветитьУдалить
  3. @Pasha

    Это можно сказать контекст запроса, а фактически DTO для передачи критериев запроса.

    ОтветитьУдалить
  4. @Idsa

    Еще есть один нюанс - уменьшается количество кода. Опять же на любителя.

    ОтветитьУдалить
  5. Александр, ты о каком коде? О тех трех строчках на каждую зависимость:

    privat readonly IQuery1 _query1;

    public SomeType(IQuery1 query1)
    {
    _query1 = query1;
    }

    Да, неприятный момент. Меня напрягает, когда прокидывание зависимостей - портянка на полэкрана. Кажется, Мартин писал, что если больше 3-4 зависимостей, то это уже говнокод, и его нужно рефакторить. Но рефактиронг в сторону IQueryFactory - еще больший говнокод (пусть и удобный). Хм...

    ОтветитьУдалить
  6. @Idsa

    Вот это откуда:

    > О тех трех строчках на каждую зависимость:
    privat readonly IQuery1 _query1;

    public SomeType(IQuery1 query1)
    {
    _query1 = query1;
    }

    ОтветитьУдалить
  7. @Александр

    Это просто пример пробрасывания непосредственно Query, а не QueryFactory

    ОтветитьУдалить
  8. @Idsa

    Так не получится, потому что IQuery* будет очень много.

    ОтветитьУдалить
  9. @Александр

    Их будет столько же, сколько и методов в IQueryFactory.

    ОтветитьУдалить
  10. @Idsa

    В ссылках, которые я привел у IQueryFactory есть только один метод не зависимо от кол-ва объектов Query*

    ОтветитьУдалить
  11. Ну ок. Если отталкиваться от статей, то будет прокидываться не IQuery1, а IQuery<SomeCriterion, SomeResult>.

    ОтветитьУдалить
  12. @Idsa

    Я не понимаю куда это будет прокидываться :)

    Контроллеры, например, будут принимать только IQueryFactory, как и все остальные объекты в системе.

    ОтветитьУдалить
  13. @Александр

    Так я рассматриваю ситуацию, когда QueryFactory нет

    ОтветитьУдалить
  14. @Idsa

    А, когда ее нет слишком много инжектировать придется. Для однотипных объектов всё-таки надо использовать фабрику.

    ОтветитьУдалить
  15. @Александр

    В общем случае ты, наверное, прав. Но так ли много Query будет в одном сервисе/контроллере? 2, 3, 5? Насколько для нас важно явно видеть, от каких запросов зависит сервис (тестировать такие классы все же удобнее)? От ответов на эти вопросы, думаю, зависело бы мое решение, если бы я вдруг собрался переползти с IQueryable+Specifications на Queries.

    ОтветитьУдалить
  16. @Idsa

    Допустим в начала их будет по 3-4 в каждом контроллере, а потом по 10-12. Будешь рефакторить?

    Очевидно же, что кол-во запросов увеличивается с ростом проекта.

    ОтветитьУдалить
  17. >>Будешь рефакторить?

    Скорее всего, да

    >>Очевидно же, что кол-во запросов увеличивается с ростом проекта.

    Количество запросов в целом - да, увеличивается. Количество запросов в конкретном сервисе/контроллере - не факт. По крайней мере их увеличение (как и любых других зависимостей) - отличной повод задуматься о рефакторинге.

    ОтветитьУдалить
  18. @Александр

    Пожалуй, ты меня убедил :)

    ОтветитьУдалить
  19. Алексей, вы пропустили всё, о чем говорилось в 
    http://blog.byndyu.ru/2011/08/repository.html и комментариях к этой статье.

    Прежде, чем вы перечитаете статью и комментарии могу сразу сказать пару вещей.

    В вашем решение IDatabaseFactory возвращает DataContext, а это значит, что работа с данными будет раз и навсегда привязана к EF. Что вы будете делать, если надо будет менять ORM или часть объектов хранить не в БД?

    Если надо сделать сложную выборку, которая будет не просто Where, а еще сортировку, join т.п., в какой метод репозитория вы это передадите?

    Если вам нужно сделать подгрузку связных сущностей (Fetch), где вы будете это указывать?

    Описанный вами подход можно назвать Generic Repository со всеми его известными проблемами. Он подходит, если у вас маленькие проекты, которые никогда не надо поддерживать, либо если у вас большие проекты, которые можно делать бесконечно долго. Это не гибкое решение, которое ведет к техническим долгам.

    ОтветитьУдалить
  20. Почему то сбивается разметка в приведенном выше коде - должно быть так

    IEnumerable GetMany(Expression> where);

    ОтветитьУдалить
  21. Всё равно сбивается

    ОтветитьУдалить

Создаем IT-продукты на заказ

За 5 лет мы создали продукты с топовыми e-commerce компаниями, ритейлом, банками и другими бизнесами по всему миру.

Специализируемся на SaaS-решениях на архитектуре микросервисов, делаем аналитику IT-проекта перед стартом.

Посмотрите, что говорят о нас клиенты и как комфортно мы стартуем проекты.

Для обсуждения проекта пишите на ceo@byndyusoft.com или звоните +7 (904) 305 5263