Комментарии 24
Явные контракты — теперь мы чётко видим и понимаем, какие данные принимает приложение при создании заказа и какие данные возвращает
StoreOrderFeatureTest
app/UseCases/V1
StoreOrderHandler
testUserCanCreateOrder
у разрабов на laravel какая то особая тяга к хаосу.
фичатест тестирует контроллер, а не фичу
фича - сохранить заказ (не обработать, не принять, не поставить в очередь на обработку)
название тест метода говорит чтт проверяется то что пользователь может ссоздать заказ, хотя фича сохранить заказ
фича сохранить заказ проверяется в тесте контроллера апишеи, который проверяет может ли пользователь создать заказ
фича сохранить заказ = обработать запрос "сохранить", вызвав обработчик сохранения заказа, который на самом деле юзкейс
тест фичи "сохранить заказ" проверяет что "пользователь может создать заказ", проверяя что "после вызова запроса апи от юзера 1 в базе есть заказ юзера 1"
плавает как утка, крякает как дятел, ведет себя как покемон, но в целом - енот охотничей породы
Тестирование UseCase? - это ещё проще!
В данном случае я советую писать тесты которые проверят работоспособность вашего бизнес-процесса.
барабанная дробь... да, тестируется именно контроллер, все эти юзкейсы инпуты аутпуты были зря.
вопросы:
а зачем тогда вам были нужны все эти церемонии с выделением input/output/handler?
что, кроме бессмысленных в приведенном примере 3+ классов, вы потеряете в этом случае, если контроллер просто сохранит заказ из данных полученных в запросе?
как вы собираетесь использовать версионирование юзкейса "сохранить заказ"? будет ли ситуация что их 2 параллельно работают?
учиться и учиться!
Капец. Если б не ваша статья я бы считал что usecases я сам придумал неделю назад. Я их назвал прям так же. Использовал схожим образом. И был настолько рад этой идее что мне даже не пришло в голову нагуглить что-то подобное:)
Большое спасибо что прочитали)
Да UseCase-ы как оказалось уже стандарт, особенно их ценят в бигтех, так как бизнес-процессы там большие и запутаться в них легко.
Если не считать того, что Rational Unified Process был опубликован, кажется, в 1998, и, следовательно, прорабатывался в целом в конце прошлого века, и да, там есть юз кейзы, вы оба молодцы. :)
Так то usecase не должны возвращать результаты, результат выполнения события, посмотрите видео с DrupalCon которому уже 10лет, там все подробно разложено https://youtu.be/ajhqScWECMo
Большое спасибо что прочитали статью)
Не могу отрицать такие реализации тоже имеют место быть, но в данном случае мы работаем с restAPI приходиться что-то возвращать)
Если речь о Rest API, то в некоторых случаях можно возвращать 204 No Content :)
Имхо ситуация отличается от проекта к проекту, тут сложно предугадать.
Если можно сформировать DTO'шку из текущего агрегата, почему этого не сделать? Такой код не будет делать лишние запросы в базу и, скорее всего, его будет просто понять. Тут скорее важно только где и как вы мапите в DTO и что бы сам агрегат не был слишком раздут.
С другой стороны, если так поступить не получится, придётся обращаться к CQRS, который, как тут недавно писали, нарушает DDD, и делать запрос из ReadRepository.
Поэтому имхо баланс очень важен.
Согласен с вами полностью!
Когда мы ничего не возвращаем наша архитектура становится похожа больше на команды CQRS паттерн.
Но даже самые смелые возвращают id записи чтобы была возможность у фронта обратиться за получением, либо прибегают к использованию websocket чтобы получить тот или инное событие системы.
Был у нас такой юзкейсер, до сих пор разгребаем..)
Большое спасибо что прочитали статью)
Если не секрет что конкретно разгребаете? С какими проблемами столкнулись?
с проблемой полотна юзкейсов, которые оказываются избыточной прослойкой из двух доп. классов для любой операции
Понял, да конечно каждый подход надо обсуждать с командой, нужен/устраивает ли он команде/у. За-то вы знаете что умеет делать ваше приложение, могу вас обрадовать)
Что бы знать, что делает приложение, в 90% случаях достаточно посмотреть на точки входа, разве нет? Оставшиеся 10% это какие нибудь бекграунд джобы.
Имхо, не важно как вы называете это, но это просто добавление 4 слоя к стандартной трехслойной архитектуре. И имхо в идеале добавлять это только для части сценариев, где наблюдается сложная бизнес логика, связанная с множеством сервисов и репозиториев. Таких сценариев часто не очень много, подавляющее большинство вполне укладываются в три слоя.
Как мне кажется выделить какой либо сценарий в 4 слой можно на любом этапе разработки, когда в этом появится необходимость (каждый сам проведет границу).
1. Вот был у меня UseCase - создание приглашения о присоединении к системе для пользователя (в табличку запись добавляется, пользователю письмо улетает).
Потом появился новый сценарий - принятие запроса на временное присоединение к пространству пользователя специалиста поддержки, в который так же входит отправка приглашения пользователю (в табличку запись добавляется, пользователю письмо улетает). Теперь мой первый UseCase - уже не UseCase, если это часть другого UseCase? Что мне делать с "последовательностью управляющих конструкций"? Продублировать в двух UseCase'ах? Вынести код из первого UseCase в разделяемый сервис/модель? Вызвать один UseCase из другого?
2. Если сервисы делать маленькими, то это уже UseCase'ы?
Большое спасибо что прочитали статью!)
Вы можете вынести общую логику в сервис, и сервис уже подключить в двух UseCase. Использовать один UseCase в другом UseCase лучше в самую последнюю очередь.
Маленькие сервисы это не UseCase. Размер класса не определяет его роль. UseCase - это один конкретный бизнес процесс, бизнес сценарий. А Service это переиспользуемая бизнес-логика, т.е. часть какого-либо сценария.
Ну и в чём тогда выигрыш. Вот мы разложили всё по "сервисам" - переиспользуемую бизнес-логику и "use-case'ы" - конкретные бизнес-сценарий. У нас система развивается и "конкретный бизнес-сценарий" превратился в "переиспользуемую бизнес-логику". Так что границы между этими понятиями, которые мы для себя нарисовали, оказались весьма условны.
Статья напомнила мне об архитектурном паттерне Porto, с которым я недавно познакомился. Porto — это значительно более широкая концепция, нежели просто use cases: она описывает полноценную архитектуру приложения с чётким разграничением зон ответственности между компонентами.
Тем не менее, раздел про Actions и Tasks в документации Porto перекликается с идеями из этой статьи — там подробно описано, как декомпозировать бизнес-логику и куда её помещать.
Всем, кого заинтересовала тема, рекомендую ознакомиться с разделом «Components» в официальной документации Porto — думаю, это даст более широкий контекст для понимания подобных подходов.

UseCase или как описывать бизнес-процессы в коде