Exceptions

Adelf

Administrator
Команда форума
MVC: ответ определяется моделью и оформляется во view.
какое именно слово объяснить?
так какой ответ из модели в итоге формируется в 400 ошибку? У меня это эксепшен(причем специальный, если другой, то 500). у Вурдалака PostPublishingErrors - типа если пустая, то 200, если нет, то 400. А у тебя?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Ну давай попытаемся отменить заказ, который уже отменен. через API.
Оно нам должно выдать некую ошибку, что нельзя так.
допустим, статус заказа: cancelled, приходит запрос UPDATE /orders/123 {"status":"cancelled"}, логичным ответом будет 200 OK

если в запросе будет UPDATE /orders/123 {"status":"processing"} - ответ будет 403 Forbidden
 

Adelf

Administrator
Команда форума
допустим, статус заказа: cancelled, приходит запрос UPDATE /orders/123 {"status":"cancelled"}, логичным ответом будет 200 OK
ладно. трудно мне придумать нормальный пример. попробуем такой. человек хочет сделать заказ, но пока он думал, единственный экземпляр данного товара уже забрал в свой заказ другой покупатель.
Система при попытке сделать такой заказ должна дать ошибку, что не получилось. это ведь тоже 400?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
так какой ответ из модели в итоге формируется в 400 ошибку? У меня это эксепшен(причем специальный, если другой, то 500). у Вурдалака PostPublishingErrors - типа если пустая, то 200, если нет, то 400. А у тебя?
это уже становится грустно. модель не формирует ответы - ответы формирует view, а у моделей есть состояние

почитай про mvc
 

AnrDaemon

Продвинутый новичок
ой да какая разница. ты такой неабстрактный
Дело именно в том, что пытаешь абстрактно обсуждать конкретные вещи.
Пока ты не поймёшь, где у тебя проходит принципиально конкретная граница, диалога не будет.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
вот и суть топика: @Adelf генерирует вывод прямо в модели, нарушая MVC

Вурдалак написал еще в начале топика. PostService:: Publish() может вернуть DTO PostPublishingErrors, который передается во View. Или "состояние"
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
то есть, оператор return ты заменил оператором throw, в чем вопрос?
можешь yield использовать
 

Adelf

Administrator
Команда форума
Если использовать некий CommandResult объект, то вопросов никаких. Там все просто.
Попытаюсь обьяснить.
Многие фреймворки для ошибок 4xx используют эксепшены.
Http404Exception, AuthorizationException, ValidationException, etc.
Они обеспечивают удобный проброс ошибки в нужные обработчики.
Я по той же аналогии использую эксепшены для ошибок логики. И задумывался - стоит ли конкретизировать каждую ошибку создавая для нее отдельный класс эксепшена.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
я понял, это пример мнимой проблемы
вместо возврата из модели DTO объекта одного типа, и решения поставленных задач, ты возвращаешь множество разных типов в соответствии с модным принципом "We enjoy typing", реализуя один интерфейс множеством разных классов
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
дело в том, что задача, которая стоит перед авторами фреймворков, противоположна интересам реальных долгоживущих проектов

авторам фреймворков надо привлечь миллионы джуниоров, которые смогут написать сайтик или доработать существующий без чтения документации по фреймворку
все должно быть наглядно, в форме аудио-подкастов, чтобы заходило под смузи
главное - не отпугнуть, и лучше всего это получается у Laravel

Но никто не пытается возвращать ошибки валидации в форме исключений, например, в Симфони
https://symfony.com/doc/current/validation.html#using-the-validator-service
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
Возвращать статус из команды, тоже не самое правильное решение. Это подразумевает дополнительную проверку этого статуса хотябы на наличие положительного ответа.
допустим, статус заказа: cancelled, приходит запрос UPDATE /orders/123 {"status":"cancelled"}, логичным ответом будет 200 OK

если в запросе будет UPDATE /orders/123 {"status":"processing"} - ответ будет 403 Forbidden
в этом случае проверку на состояние заказа ты по непонятной причине вырезал из обработчика и влепил в контроллер для формирования вида. В случае с исключением, проверка довольно абстрактная и находится на уровне аппликации, подходит к любой команде, звучит изначально как некоректные данные без дополнительной конкретики о том что это заказ у которого есть статус который должен быть cancelled
 
Последнее редактирование:

флоппик

promotor fidei
Команда форума
Партнер клуба
Я вообще вижу пару проблем с пересылкой событий бизнес-логики через исключения:

1. Они не следуют построенному business logic flow в обработке, а вырываются из него, заставляя тебя обрабатывать их в каком-то "другом" случайном месте, часто в одном и том же, создавая место с гипер-отвественностью. Просто попробуй для таких евентов нарисовать UML диаграмму.
2. По этой же причине они теряют контекст исполнения - ты знаешь, что произошло, но не знаешь "почему" в терминах бизнес-логики - ты уже покинул контекст исполнения. Для сохранения контекста тебе придется вводить по стопицот исключений, описывающих их контексты происхождения (NotPublishedPostUserHasNoRightException, PaymentFailedPaymentGateWayTimeOuted ?) или напихивать в них дополнительные данные, делая из них полноценные события.
3. Представь просто, что у тебя появились варианты обработки для таких событий (например, при PaymentFailedPaymentGateWayTimeOuted ты вчера подключил новый шлюз, и теперь можешь попробовать пробовать еще его после ошибки) как ты будешь работать с таким эксепшном?
4. У тебя могут появится другие консьюмеры таких событий, в том числе и внешние - например, какой-нибудь там отложенный переотправщик писем. И вместо того, что бы просто отдать такое бизнес-событие в одну из очередей, ты вынужден это все где-то лепить в глобальных обработчиках.

Вкратце - эксепшнами можно передавать события, но это очень ограниченный простыми случаями инструмент.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@WMix, твой пост выглядит как набор бессвязных фраз.

Это подразумевает дополнительную проверку этого статуса
что "это"? статуса чего? проверку на что? дополнительную к чему?
хотябы на наличие положительного ответа
если мы обсуждаем проверку ответа API клиентом - то она обязательна не "хотя бы", а всегда

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

короче, протрезвей, пожалуйста
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
> 1. вырываются из него, заставляя тебя обрабатывать их в каком-то "другом" случайном месте​

чаще всего в userland обработка идет в стиле
PHP:
try{
 $res = $model->process($param);
}catch(Exception $E){
$logger->log($E->getMessage());
}
По сути, эмуляуция golang-овского возврата множества параметров, throw вместо return.

2. Для сохранения контекста тебе придется вводить по стопицот исключений, описывающих их контексты происхождения (NotPublishedPostUserHasNoRightException, PaymentFailedPaymentGateWayTimeOuted ?) или напихивать в них дополнительные данные, делая из них полноценные события.​

Контекст тут никто и не думает обрабатывать. Дырку починят космонавты в полете.

3. Представь просто, что у тебя появились варианты обработки для таких событий (например, при PaymentFailedPaymentGateWayTimeOuted ты вчера подключил новый шлюз, и теперь можешь попробовать пробовать еще его после ошибки) как ты будешь работать с таким эксепшном?​

У движения we enjoy typing есть адепты. Если ты не нажимаешь Ctrl-V, а набираешь код десяти исключений руками - все хорошо.

4. ты вынужден это все где-то лепить в глобальных обработчиках.​

для этого им дали специальный инструмент
https://laravel.com/docs/5.6/middleware
 

Adelf

Administrator
Команда форума
@флоппик первый ответ по теме :)
"что-то случилось с платежным шлюзом" или "email не отправился" это не эксепшены домена. это инфраструктурные эксепшены. их нет смысла обсуждать.

А такие ситуации как "не могу отменить уже отмененный заказ" как раз видимо не имеет смысла делать отдельными исключениями. Всю логику отмены заказа содержит $order->cancel() метод. Только он знает что и как происходит когда заказ пытаются отменить. и уж если он не может отменить покакой-то причине, то никто другой в приложении не сможет. Это надо знать внутреннюю структуру объекта Order. И врядли у класса Order есть метод tryToDoSOmethingWithCancelOrderException(Exception $e). Поэтому нет смысла плодить классы. Достаточно одного какогонибудь \LogicException, хотя я и предпочел бы свой... DomainException.
То, что это прерывает контекст выполнения - нормально и даже удобно. Мы дальше не можем выполнить это действие, просто бросается исключение и все. Оно может выброшено каким-то приватным методом внутрях класса. И не надо его по цепочке наверх передавать через ретурны как в го.

для этого им дали специальный инструмент
не для этого
 

флоппик

promotor fidei
Команда форума
Партнер клуба
@флоппик первый ответ по теме :)
"что-то случилось с платежным шлюзом" или "email не отправился" это не эксепшены домена. это инфраструктурные эксепшены. их нет смысла обсуждать.
Не согласен. Ответ 502 Gateway Timeout при попытке соединиться со шлюзом - инфраструктурый эксепшн. Событие "Не смогли отправить данные о платеже" которое пролучилось из этого инфраструктурного эксепшна - ошибка во время бизнес-процесса и часть бизнес-процесса.
SMTP rejected - инфраструктурный эксепшн. "Письмо с подтверждением не отправлено" - событие в бизнес-логике.
 

Adelf

Administrator
Команда форума
Не согласен. Ответ 502 Gateway Timeout при попытке соединиться со шлюзом - инфраструктурый эксепшн. Событие "Не смогли отправить данные о платеже" которое пролучилось из этого инфраструктурного эксепшна - ошибка во время бизнес-процесса и часть бизнес-процесса.
SMTP rejected - инфраструктурный эксепшн. "Письмо с подтверждением не отправлено" - событие в бизнес-логике.
в моем представлении так: Идет процесс оплаты и только когда оплата дошла, делается order->markAsPayed()
Тоже самое с письмом. Если нам важно так это письмо, то когда оно отправлено - вызываем order->emailIsSent() какой-нибудь.

Если не произошло, то и домена не коснется. Потом, какой-нибудь процесс-чистильщик пройдется по неоплаченным заказам и заказ сам отменится если оплата не прошла. как-нибудь так. не будет каких-либо эксепшенов или error-статусов в домене.
 
Сверху