В комментах задали вопрос:
А есть какое-то описание принципов обработки ошибок в ЭП? Типа где обычные исключения, бизнес исключения, где возврат значения (Result?) и почему и т.п.
И ответ получился размером с небольшой пост, так что решил запулить его в канал.
Прямо сейчас стратегия обработки ошибок не то, что не описана, но даже не детализирована.
Но общее видение такое:
Я исхожу из того, что существует 4 типа ошибок:
1. Ошибки оборудования
2. Ошибки программиста бэка
3. Ошибки программиста фронта/поломки обратной совместимости
4. Ошибки, которых не должно быть.
Ошибки оборудования и внешних сервисов
Это отказы жёстких дисков, сети, внешних сервисов и т.д. Их чинят девопсы. Или программисты внешних сервисов. И для починки нужен редеплой (вплоть до физического развёртывания нового сервера) оборудования или внешнего сервиса
Ошибки программиста бэка
Логические ошибки - NullPointer, IllegalArgument, IndexArrayOutOfBounds и т.д. Их чинят разрботчики бэка и для починки требуется редеплой бэка.
Ошибки программиста фронта
Все ошибки когда запрос кривой - вообще не HTTP, HTTP, но не ложится на модель бэка, на модель ложится но является внутренне противоречивым (этого лучше избегать). Их чинит разработчик фронта и для починки требуется редеплой фронта.
Ошибки которых не должно быть (ака доменные ошибки)
Это когда софт работает "нормально", но пользователь с его помощью пытается сделать то, чего нельзя. Их чинит юзер, без какого-либо редеплоя.
Возьмём пример из Trainer Advisor, где при создании приёма есть проверка на то, что нельзя создать для одного терапевта приём, пересекающейся с существующим. Но для лучшей иллюстрации давайте представим, что речь идёт не о терапевте, а о зале - нельзя создать два приёма в одном зале, которые пресекаются по времени.
И вот сейчас у меня это реализовано максимально дёшево - я проверяю наличие приёмов на запросе создания и если он есть - показываю ошибку юзеру
Более дорогой вариант позволил бы избежать избежать этой ошибки для одного терапевта - если бы я проверку выполнял фоном в процессе заполнения формы и просто блокировал бы кнопку подтверждения в случае попытки создать пересекающийся приём, то терапевт физически не смог бы сделать фигню (считаем, что терапевты не владеют curl-ом)
Ещё более дорогой вариант позволил бы минимизировать вероятность такой ошибки и для двух терапевтов - если бы при появлении нового приёма в системе я бы рассылал инфу об этом всем клиентам у которых сейчас открыто окно создания приёма, и на фронте бы блокировал кнопку подтверждения при обнаружении пересечения.
А ещё можно сделать обмен данными с форм создания/редактирования, чтобы... Ладно, в общем я утверждаю, что вероятность подобных ошибок можно довести до столь малой, что их можно "упросить" до ошибок оборудования или разработчика бэка.
И вот исходя из этой классификации у меня общая стратегия следующая:
1. Ошибки оборудования и разработчика бэка - по максимуму игнорим. Они вылетают исключениями из библиотечного кода и прямой наводкой летят в глобальный обработчик ошибок, который покажет юзеру "У нас проблемы, работаем, попробуйте позже"
2. Ошибки разработчика фронта - в идеале аналогично, но только лучше веб-фреймворк настроить так, чтобы он сам с ними разбирался.
3. А вот "доменные" ошибки - в идеале должны возвращаться в виде Result-а и контроллер/фронт должны показать юзеру понятную ошибку и дать инструкции по её устранению
Однако есть нюанс - Spring не дружит c Result. И если вернуть ошибку в виде Result, а не исключения - он транзакцию не откатит. Это можно захачить кастомной аннотацией, но не хочется.
Поэтому текущие правила работы с ошибками у меня такие:
1. Ошибки оборудования я стараюсь по максимуму игнорить.
2. Ошибки разрботки бэка (помимо тех, что рантайм мечет) я мечу стандартными error, check, require и т.д. и они улетают наружу 500-ками
3. Бизнес-логика возвращает ошибки Result-ом (в TA такого ещё нет)
4. Оркестрация выбрасывает ошибки (в том числе Result от бизнес-логики) исключениями
5. Контроллеры ловят исключения в Result и разбирают его в функциональном стиле