Если коротко, то да. Ведь суть подхода сводится к тому, чтобы в случае ошибки дообогатить ее исключением, содержащим код ошибки. Варианты как это сделать:
Как вы правильно отметили, можно декомпозировать операцию авторизации на методы и переложить работу на аспекты
Можно использовать классический подход и выкидывать "свои" UserNotFoundException, а уже в @ControllerAdviceприсваивать этим ошибкам код (и текст)
Можно написать обработчик try-catch с добавлением Exception с кодом ошибки в suppressed вручную, например через статик метод.
У нас есть очень небольшой процент таких кейсов, и как-правило мы идем по 1 или 2 пути
В нашем случае любая ошибка должна считаться ошибкой бизнес-логики и должна сопровождаться двумя сообщениями: 1 - техническое, оригинальный текст сообщения, 2 - бизнес ошибка, какая операция не может быть выполнена и почему (этот же текст мы должны показать пользователю на фронте)
При такой постановке задачи, мы пытались вначале поймать и обернуть ошибки как можно ближе к месту возникновения, а затем стали дообогащать оригинальные исключения кодом ошибки (и текстом, соответствующим этому коду)
Если просто перехватывать ошибки на уровне контроллера, то, например, RestClientException нам не скажет ничего о системе, к которой мы пытались обратиться с помощью http-клиента и бизнес-операции, которую пытались выполнить
ProblemDetails, как реализация стандарта RFC 9457, описывает представление ответа в случае ошибки. В рамках данной статьи я пытался сделать упор на подкапотную составляющую. Понятное дело, что имея необходимую информацию об ошибке, можно представить ее в любом удобном виде
У нас в проектах они не используются, поэтому не было смысла переходить на более низкий уровень. Тем не менее, всплывающее исключение имеет всю информацию и может быть обработано аналогичным образом в фильтре
Про авторизацию в микросервисах это тема отдельной статьи, и универсального рецепта не существует. Тут прежде всего стоит исходить из проекта и корпоративной архитектуры.
Как правило, в систему должен приходить уже авторизованный запрос, а за саму авторизацию отвечает отдельный микросервис, гейтвей или даже целая система. Да и видов авторизации пользователей может быть одновременно несколько: логин/пароль, jwt-токен, ldap, сертификат, двухфакторка и т.д.
После авторизации информацию о пользователе можно, например, сохранить в кэш. (Но в целом ничего страшного и запрашивать пользователя по идентефикатору при необходимости из сервиса, хранящего пользователя) Между микросервисами можно передавать идентефикатор пользователя, jwt-токен или идентефикатор временной записи в кеше, по которой мы можем получить полтщователя.
Библиотеки с интерфейсами и моделями (api) каждого микросервиса у нас как раз публикуется в приватном репозитории, и используется в других микросервисах для построения клиентов. При этом мы всегда можем знать, к какому микросервису относится модель и где она используется
Я вижу в создании таких клиентов одну большую проблему - огромное количество типового, шаблонного кода, который приходится писать вручную. И тут нужно либо смириться, либо написать библиотеку с оберткой для клиентов (и описанием исключений возможно), либо переходить на генерацию клиентов по swagger. Помимо этого, удобно использовать аннотации spring boot 3 для создания клиентов на основе интерфейсов
Модуль client это реализация API, которая вызывает сервисный код, или уже готовый клиент к микросервису?
Мы пытались делать REST-клиент, как стартер, поставляемый с микросервисом, но позже отказались от этой идеи. Версии спринга могут различаться, усложняется миграция на новую версию. Поэтому пришли к выводу, что нужно поставлять только интерфейсы для создания и клиента, и серверного АПИ для реализации
Мы долго шли по этому пути (как я попытался описать в статье), и до какого-то момента нас это тоже устраивало. Но самыми значимыми минусами стало:
Java класс содержащий коды ошибок. Он был в общей либе для всех сервисов, т.е при добавлении нового кода ошибки, надо было эту библиотеку менять
Нужно не забывать везде блоки try-catch
Помимо ApiException нужно еще и корневой Exception описать, потому что где-нибудь про блок try-catch забудешь
Лишняя логика по обработке и лишняя логика в тестах
Самое главное - от нас бизнес стал требовать разную логику по формированию сообщений в случае разных типов ошибок. Поэтому появились instanceOf в коде
Понятно, что все очень сильно зависит от проекта и требований, и никакого идеального решения не будет никогда.
Если коротко, то да. Ведь суть подхода сводится к тому, чтобы в случае ошибки дообогатить ее исключением, содержащим код ошибки. Варианты как это сделать:
Как вы правильно отметили, можно декомпозировать операцию авторизации на методы и переложить работу на аспекты
Можно использовать классический подход и выкидывать "свои" UserNotFoundException, а уже в @ControllerAdviceприсваивать этим ошибкам код (и текст)
Можно написать обработчик try-catch с добавлением Exception с кодом ошибки в suppressed вручную, например через статик метод.
У нас есть очень небольшой процент таких кейсов, и как-правило мы идем по 1 или 2 пути
В нашем случае любая ошибка должна считаться ошибкой бизнес-логики и должна сопровождаться двумя сообщениями: 1 - техническое, оригинальный текст сообщения, 2 - бизнес ошибка, какая операция не может быть выполнена и почему (этот же текст мы должны показать пользователю на фронте)
При такой постановке задачи, мы пытались вначале поймать и обернуть ошибки как можно ближе к месту возникновения, а затем стали дообогащать оригинальные исключения кодом ошибки (и текстом, соответствующим этому коду)
Если просто перехватывать ошибки на уровне контроллера, то, например, RestClientException нам не скажет ничего о системе, к которой мы пытались обратиться с помощью http-клиента и бизнес-операции, которую пытались выполнить
ProblemDetails, как реализация стандарта RFC 9457, описывает представление ответа в случае ошибки. В рамках данной статьи я пытался сделать упор на подкапотную составляющую. Понятное дело, что имея необходимую информацию об ошибке, можно представить ее в любом удобном виде
У нас в проектах они не используются, поэтому не было смысла переходить на более низкий уровень. Тем не менее, всплывающее исключение имеет всю информацию и может быть обработано аналогичным образом в фильтре
Про авторизацию в микросервисах это тема отдельной статьи, и универсального рецепта не существует. Тут прежде всего стоит исходить из проекта и корпоративной архитектуры.
Как правило, в систему должен приходить уже авторизованный запрос, а за саму авторизацию отвечает отдельный микросервис, гейтвей или даже целая система. Да и видов авторизации пользователей может быть одновременно несколько: логин/пароль, jwt-токен, ldap, сертификат, двухфакторка и т.д.
После авторизации информацию о пользователе можно, например, сохранить в кэш. (Но в целом ничего страшного и запрашивать пользователя по идентефикатору при необходимости из сервиса, хранящего пользователя) Между микросервисами можно передавать идентефикатор пользователя, jwt-токен или идентефикатор временной записи в кеше, по которой мы можем получить полтщователя.
Библиотеки с интерфейсами и моделями (api) каждого микросервиса у нас как раз публикуется в приватном репозитории, и используется в других микросервисах для построения клиентов. При этом мы всегда можем знать, к какому микросервису относится модель и где она используется
Я вижу в создании таких клиентов одну большую проблему - огромное количество типового, шаблонного кода, который приходится писать вручную. И тут нужно либо смириться, либо написать библиотеку с оберткой для клиентов (и описанием исключений возможно), либо переходить на генерацию клиентов по swagger. Помимо этого, удобно использовать аннотации spring boot 3 для создания клиентов на основе интерфейсов
Модуль client это реализация API, которая вызывает сервисный код, или уже готовый клиент к микросервису?
Мы пытались делать REST-клиент, как стартер, поставляемый с микросервисом, но позже отказались от этой идеи. Версии спринга могут различаться, усложняется миграция на новую версию. Поэтому пришли к выводу, что нужно поставлять только интерфейсы для создания и клиента, и серверного АПИ для реализации