Application Layer нужен, чтобы явно выделить все действия, которые может выполнять система, и собрать их в одном месте, а не размазывать по контроллерам, кронам и очередям.
Такие действия обычно называют юзкейсами (use cases). В их названии используется глагол: CreateTaskUseCase, OpenProjectUseCase, CloseProjectUseCase. В зависимости от архитектуры также могут встречаться наименования interactors, actions или application services.
Задача юзкейса — выполнить последовательность действий для достижения бизнес-результата. Например, для закрытия задачи — прочитать задачу из БД, закрыть и сохранить. В реальном сценарии часто требуется выбросить исключение, если задача не найдена или уже закрыта.
Практические рекомендации:
UseCase может быть переиспользован, если бизнес-сценарий идентичен, а меняется только способ вызова — по кнопке или по крону, из веб-контроллера или из API-контроллера. Однако для каждого нового бизнес-сценария обычно стоит создать отдельный UseCase, даже если результат выглядит похожим — например, создание задачи, импорт задач или создание запланированной задачи, — поскольку бизнес-логика таких сценариев может отличаться.
UseCase обычно реализуется отдельным классом, а не методом внутри другого класса. Это улучшает читаемость проекта (Screaming Architecture), упрощает тестирование и облегчает дальнейшую перегруппировку кода. В качестве единой точки входа обычно используется один публичный метод, например handle или invoke.
Для типизированного входа и выхода хорошо подходят объекты (DTO), а не представления (XML, JSON, HTML). CreateTaskDto и CreateTaskResult могут использоваться в качестве входного и выходного объектов для CreateTaskUseCase. Все классы, относящиеся к юзкейсу (входной DTO, обработчик и выходной DTO), располагаются в отдельной директории этого юзкейса.
Зависимости UseCase (репозитории, клиенты, внешние API и т.д.) передаются через интерфейсы. Их реализации находятся вне Application Layer, чаще всего в Infrastructure.