А что вы делаете с дублированием кода в Application Service?

Обсуждаем, как правильно строить приложения
Ответить
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

А что вы делаете с дублированием кода в Application Service?

Сообщение glagola »

Через какое-то время начал замечать, что постоянно пишу однотипный код при создании Application services. Чтобы не быть голословным приведу пример из текущего проекта.

Прежде чем начинать работать с каким-то пользователем в рамках Application service:
  • Достать его из репозитория, если не найден - кинуть исключение UserNotFound
  • Проверить, а не забанен ли данный пользователь, если забанен - кинуть исключение UserBanned

Эта логика повторяется из сервиса в сервис, не выдержав я вынес ее в доменный сервис, который просто возвращает пользователя по идентификатору.

Пока все работает и проблем не доставляет, но меня терзают какие-то сомнения, насколько это верно с точки зрения DDD?

P.S. а как вы боритесь с подобным дублированием?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение zelenin »

ну это не совсем дублирование - это необходимые для кейса действия.
UserNotFound может репозиторий кинуть.
UserBanned очевидно не универсальная проверка, т.к. не всегда надо проверять.

Вообще да, вероятно я тоже бы вынес в сервис-фетчер.
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение glagola »

zelenin писал(а): 2017.04.04, 21:18 ну это не совсем дублирование - это необходимые для кейса действия.
Согласен, не совсем дублирование, но решение оказалось аналогичным решению проблемы дублирования кода.
zelenin писал(а): 2017.04.04, 21:18 я тоже бы вынес в сервис-фетчер
Благодарю за то, что напомнили про термин "fetcher" - переименовал свой сервис :)
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение anton_z »

Еще есть такое понятие - супертип слоя (Layer supertype, Fowler). В данном случае это базовый класс для application services. Можно общие методы туда. Но это наследование. Еще есть трейты, но там надо аккуратно с зависимостями.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение slavcodev »

Или можно во всех сервисах у репозитория запросить "незабанненого" юзера по ИД и в репозитории кидать оба исключения по необходимости.
Жду Yii 3!
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение glagola »

slavcodev писал(а): 2017.04.05, 02:24 Или можно во всех сервисах у репозитория запросить "незабанненого" юзера по ИД и в репозитории кидать оба исключения по необходимости.
А факт генерации исключения UserBanned из репозитория не будет являться "протеканием" бизнес логики в инфраструктуру? Я в том смысле, что если решим переписать репозиторий, например под другое хранилище, можем забыть про то, чтобы бросать данное исключение.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение zelenin »

glagola писал(а): 2017.04.05, 13:33
slavcodev писал(а): 2017.04.05, 02:24 Или можно во всех сервисах у репозитория запросить "незабанненого" юзера по ИД и в репозитории кидать оба исключения по необходимости.
А факт генерации исключения UserBanned из репозитория не будет являться "протеканием" бизнес логики в инфраструктуру?
будет
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение slavcodev »

Репозиторий - часть домен, не инфраструктуры.
Я в том смысле, что если решим переписать репозиторий, например под другое хранилище, можем забыть про то, чтобы бросать данное исключение.
Звучит странно для меня. Точно так же можно "забыть" методом "getById" вернуть сущность с нужным ИД.
Репозиторий это домен, если домен требует вернуть "незабаненного" юзера или бросить некоторые исключения,
значит все имплементации данного репозитория должны выполнять данные условия.
Жду Yii 3!
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение glagola »

slavcodev писал(а): 2017.04.05, 16:53 Репозиторий это домен
Поправьте если не прав, но интерфейс репозитория - домен. Реализация репозитория ничем не обязана домену, кроме как реализовывать данный интерфейс.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение slavcodev »

Хотя реализация "дело вкуса" и может не зависеть от инфраструктуры, я имел виду именно интерфейс репозитория.
Если интерфейс утверждает что будет получен именно незабаненный юзер, значит любая реализация должна реализовывать этот интерфейс.
Я привел пример с часто существующем "getById" (ну или как кому нравится называть это метод), который по условиям интерфейса должен вернуть юзера по ИД или бросить исключение об его отсутствии. Все это интерфейс диктуемый доменом.

Если тебя смущает что исключения прописаны в доке и из названия метода вроде "getGoodGuyOrGal()" не явно что нужны какие-то исключение, можно всегда расширить название "getGoodGuyOrGalOrThrowBannedException()" :mrgreen:
Жду Yii 3!
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение glagola »

slavcodev писал(а): 2017.04.05, 19:28 Я привел пример с часто существующем "getById" (ну или как кому нравится называть это метод), который по условиям интерфейса должен вернуть юзера по ИД или бросить исключение об его отсутствии. Все это интерфейс диктуемый доменом.

Если тебя смущает что исключения прописаны в доке и из названия метода вроде "getGoodGuyOrGal()" не явно что нужны какие-то исключение, можно всегда расширить название "getGoodGuyOrGalOrThrowBannedException()"
Мне кажется, ни документация, ни название метода не остановят человека от ошибки, так как человек может просто не проверить все варианты развития событий в коде (например тестить метод только на нормальных пользователях или только на заблокированных). А вот ошибка интерпретации (не соответствие сигнатуры метода интерфейса и класса) - 100% средство. Поэтому предпочитаю репозитории, которые либо возвращают инстанс модели либо возвращают null. К тому же, учитывая кол-во проверок/исключений, может случится так, что либо будет много высокоспецифичных методов у репозитория, либо ее вовсе будет невозможно реализовать.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение slavcodev »

glagola писал(а): 2017.04.06, 11:16 Мне кажется, ни документация, ни название метода не остановят человека от ошибки
Человека воообще мало что останавливает, учитывая текущее состоянии мира.
glagola писал(а): 2017.04.06, 11:16 К тому же, учитывая кол-во проверок/исключений, может случится так, что либо будет много высокоспецифичных методов у репозитория, либо ее вовсе будет невозможно реализовать.
И что если? Это и есть предназначение репозитория, сконцентрировать работу со списком сущностей одного типа.
Дублирование логики (проверки бана у пользователя) в кучи сервисах хуже/неудобнее чем репозиториях, имплементаций которых на самом деле не так уж и много.
glagola писал(а): 2017.04.06, 11:16 Поэтому предпочитаю репозитории, которые либо возвращают инстанс модели либо возвращают null.
Я бы посоветовал отказаться от методов которые возвращают не явные данные, но не хочу твое предпочтения менять. Каждому свое, кому-то тортики нравится, а кто-то их терпеть не может.
Жду Yii 3!
glagola
Сообщения: 47
Зарегистрирован: 2017.02.22, 19:43

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение glagola »

slavcodev писал(а): 2017.04.06, 13:10 Я бы посоветовал отказаться от методов которые возвращают не явные данные
Что значит "не явные"? Чтобы не быть голословным, вот пример сигнатуры метода из интерфейса:

Код: Выделить всё

interface IUserRepository
{
    // ...
    public function ofId(UserId $id): ?User;
}
Данная сигнатура, жестко регламентирует возвращаемые данные "null|User", тут все явно, или вы о чем-то другом?
slavcodev писал(а): 2017.04.06, 13:10 Это и есть предназначение репозитория, сконцентрировать работу со списком сущностей одного типа.
Именно, но учитавая кол-во исключений, которые нужно бросить, кол-во этих методов еще возрастет в несколько раз.
slavcodev писал(а): 2017.04.06, 13:10 Дублирование логики (проверки бана у пользователя) в кучи сервисах хуже/неудобнее чем репозиториях, имплементаций которых на самом деле не так уж и много.
Ранее в этой ветке был предложен способ, который позволяет избавиться от дублирования: вынести логику проверки бана в отдельный фетчер (доменный сервис).
slavcodev писал(а): 2017.04.06, 13:10 но не хочу твое предпочтения менять. Каждому свое, кому-то тортики нравится, а кто-то их терпеть не может.
Если у вас будет время, я был бы признателен, если бы вы смогли описать ваш подход.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение zelenin »

на самом деле лучше репозиторию оставить одну ответственность - доступ к хранилищу (get/save), без сайд-эффектов, а бизнес-проверки делать в сервисе. Мы будем точно уверены, что репозиторий либо вернет сущность, либо ее нет вовсе.
У нас может быть 5 сервисов, работающих с репозиторием, 3 из которых требуют проверки на бан, а 2 не требуют. И лучше явно в требуемом месте сделать проверку, чем иметь два репозитория, один из которых выкидывает исключение, а другой нет (с дублирующейся остальной функциональностью).
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение slavcodev »

Лучше иметь один репозиторий и два метода, по одному на диктуемую доменом условие, а не дублировать проверки в сервисах.
Жду Yii 3!
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение zelenin »

slavcodev писал(а): 2017.04.06, 16:52 Лучше иметь один репозиторий и два метода, по одному на диктуемую доменом условие, а не дублировать проверки в сервисах.
тогда у нас репозиторий раздуется, т.к. много чего можно вынести из сервиса на сторону репо.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение slavcodev »

Беспокоишься о нем, что лопнет?
Жду Yii 3!
Аватара пользователя
rugabarbo
Сообщения: 1063
Зарегистрирован: 2015.06.21, 16:21
Контактная информация:

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение rugabarbo »

glagola писал(а): 2017.04.04, 20:41 Через какое-то время начал замечать, что постоянно пишу однотипный код при создании Application services. Чтобы не быть голословным приведу пример из текущего проекта.

Прежде чем начинать работать с каким-то пользователем в рамках Application service:
  • Достать его из репозитория, если не найден - кинуть исключение UserNotFound
  • Проверить, а не забанен ли данный пользователь, если забанен - кинуть исключение UserBanned

Эта логика повторяется из сервиса в сервис, не выдержав я вынес ее в доменный сервис, который просто возвращает пользователя по идентификатору.

Пока все работает и проблем не доставляет, но меня терзают какие-то сомнения, насколько это верно с точки зрения DDD?

P.S. а как вы боритесь с подобным дублированием?
Помимо UserNotFound и UserBanned в моих сервисах встречаются ещё UserNotActivated, UserNotVerified и UserDeleted. Причём, как уже писал zelenin, разным сервисам нужны разные комбинации проверок этих состояний (особенно сильно различается логика сервисов на фронте и в админке). Поэтому я не борюсь с подобным "дублированием". Это дублирование лишь "техническое" (тупо совпадение кода), а не "логическое".

Выносить в репозитории эти выборки не вижу смысла, потому что разным сервисам нужные разные комбинации этих состояний, поэтому в сервисах проверять всё это гибче. Но для часто используемых комбинаций (например, получить всех проверенных, активированных, незабаненных, неудалённых) наверно удобно сделать и в репозитории единый метод выборки.
Последний раз редактировалось rugabarbo 2017.04.06, 19:08, всего редактировалось 2 раза.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение zelenin »

slavcodev писал(а): 2017.04.06, 18:23 Беспокоишься о нем, что лопнет?
беспокоюсь о чистоте кода. никому же не нравятся классы на тысячи строк.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: А что вы делаете с дублированием кода в Application Service?

Сообщение zelenin »

rugabarbo писал(а): 2017.04.06, 19:06Это дублирование лишь "техническое" (тупо совпадение кода), а не "логическое".
в точку
Ответить