AnrDaemon
Продвинутый новичок
Домен отправил и забыл… да-да.Если не произошло, то и домена не коснется.
Может, стоит уже проснуться?
Домен отправил и забыл… да-да.Если не произошло, то и домена не коснется.
Возвращать статус из команды, тоже не самое правильное решение. Это подразумевает дополнительную проверку этого статуса хотябы на наличие положительного ответа.
Возвращать статусчто "это"?
статус ответа командыстатуса чего?
на наличие положительного ответапроверку на что?
к вызову командыдополнительную к чему?
нет, создание этого ответа, для началаесли мы обсуждаем проверку ответа API клиентом
в этомв каком этом?
допустим, статус заказа: cancelled, приходит запрос UPDATE /orders/123 {"status":"cancelled"}, логичным ответом будет 200 OK
если в запросе будет UPDATE /orders/123 {"status":"processing"} - ответ будет 403 Forbidden
правильно, потому как ты умалчиваешь условие когда ты возвращаешь http 400в моих постах нет упоминаний ни о проверках, ни про обработчики - ни единого
$result = $orderService->cancel(...);
if($result->status !== 'cancelled'){
http_response_code(400);
}
мне даже больше интересно, почему у него 403 возвращается в одном из моментов. У него домен знает про http коды? или такая продвинутая авторизация... что вообще все тогда можно 403 ошибкой возвращать. а не 400. По моему 403 когда пытаются сделать что-то сделать с отмененным заказом... вроде и правильно, но это пример когда авторизация сильно пролезла в домен. не самый лучший вариант.правильно, потому как ты умалчиваешь условие когда ты возвращаешь http 400
он о REST вероятно, ресурс: order-123 запрещен.. на самом деле не суть, разговор то больше про 4xx кодпочему у него 403
у нас чаще 403 появляется когда вызывают action на который нет прав.По моему 403 когда пытаются сделать что-то сделать с отмененным заказом
вопрос интерпретации что такое ресурс, заказ (с точки зрения rest) или сама команда cancelОшибка, что заказ в принципе не может быть запущен заново, если отменен, вне зависимости от всяких авторизаций выглядит гораздо более логично.
class NotAllowedException extends DomainException{}
$order = $repo->get(123);
$order->calncel(...); // throw new NotAllowedException
$repo->store($order);
catch(NotAllowedException $e){
http_response_code(403);
}
а это разве не про GET/POST/PUT/DELETE?405 Method Not Allowed
может назовешь это условие, дабы наши "юношеские фантазии" поприубавитьправильно, потому как ты умалчиваешь условие когда ты возвращаешь http 400
когда заказ отменен - дальнейшая смена его статуса может быть запрещена, так что 405 здесь может быть уместен@grigori,
а это разве не про GET/POST/PUT/DELETE?
может назовешь это условие, дабы наши "юношеские фантазии" поприубавить
HTML в принципе задуман, как расширяемый протокол.а это разве не про GET/POST/PUT/DELETE?
Я не вижу связи с https://en.wikipedia.org/wiki/Information_hiding.Вроде и не про свойства тоже. Вот кому, кроме конечного юзера, может быть интересно разбирать PostPublishingErrors? Вроде никому.
А как мы это должны обрабатывать? Если там есть бизнес-процесс (сага), который по событию «Не удалось оплатить отменённый заказ» сделает компенсацию, то тогда я бы сделал ошибку в виде события.Ну это самый простой вариант того, что внутреннее состояние объекта не позволяет произвести действие. Чуть более сложный вариант - попытаться оплатить отмененный заказ.
Тут может быть 2 варианта:Ну какая разница как это называть. Да, я примерно о том же. Есть UI и он не должен позволять производить неправильные действия. Например ту же оплату отмененного заказа. Но есть же и публичные API. И там надо корректно отрабатывать все эти ошибки.
На самом деле, лучше не через exception'ы. Я имею в виду, я в своё время наелся говна с таким кейсом, когда исключение на одном уровне вполне себе BusinessException и можно рассматривать как 400, а потом контроллер чуть-чуть поменялся, часть данных стала заполняться автоматически — и тут уж никак не скажешь, что это 400, клиент вообще не виноват. А варианты тут всякие:А как нужно генерировать эту 400 ошибку? Я вот генерирую этим самым BusinessException. Точнее ловлю его в глобальном отловителе эксепшенов и отдаю нужный response с 4хх кодом.
$this
->commandBus
->execute(new PayOrder($orderId, ...))
->errors()
->map([
function (CancelledOrderCannotBePaid $error) {
throw new BadRequestException(....);
},
function (OrderWasNotFound $error) {
throw new NotFoundResource(....);
}
]);
// vs
$orderErrors =
$this
->commandBus
->execute(new PayOrder($orderId, ...))
->errors()
->reduce([
function (OrderErrors $errors, CancelledOrderCannotBePaid $error) {
return $errors->withCancelled(true);
},
function (OrderErrors $errors, OrderWasNotFound $error) {
return OrderErrors::notFound();
}
], OrderErrors::none());
return new Response($this->render($orderErrors));
Ну тут у тебя контроллер что-то уж больно умный. Сам какие-то данные достает и подставляет в запрос к service layer. Хотя если это все из HTTP-контекста не выходит(т.е. данные сохранились где-то в куках для примеру), то вроде и нормально...На самом деле, лучше не через exception'ы. Я имею в виду, я в своё время наелся говна с таким кейсом, когда исключение на одном уровне вполне себе BusinessException и можно рассматривать как 400, а потом контроллер чуть-чуть поменялся, часть данных стала заполняться автоматически — и тут уж никак не скажешь, что это 400, клиент вообще не виноват.
Клиентская валидация не является заменой серверной валидации.но вроде это уже давно не модно
ну давай уж такие истины не будем обсуждать. я говорю лишь об удобном выводе ошибок для каждого поля.Клиентская валидация не является заменой серверной валидации.
Я не понял.Ну тут у тебя контроллер что-то уж больно умный. Сам какие-то данные достает и подставляет в запрос к service layer. Хотя если это все из HTTP-контекста не выходит(т.е. данные сохранились где-то в куках для примеру), то вроде и нормально...
Ты имеешь в виду накопить $errors и выкинуть OrdersErrorsException? Ну, технически может разницы и нет. Но семантически смешивать бизнес и технические ошибки иногда становится неудобно. Здесь есть еще одна не самая очевидная проблема: если у тебя несколько вложенных bounded contexts, а пользователь «отвечает» только за самый верхний, то при вылете FooBusinessException откуда-то из глубин мы можем ошибочно решить, что нужно отдавать 400, хотя клиент может быть вовсе не виноват: то есть, одно и то же исключение может быть на уровне API отображено по-разному в зависимости от контекста.В любом случае, я не вижу большой разницы в эксепшене с тем же $errors и структуре OrderErrors.
Ну, как тебе сказать. Иногда просто ошибки и события переплетаются. Например, человек вводит неверно код из SMS в N-й раз: с одной стороны, мы должны сообщить об ошибке (CodeWasWrong) (показать капчу, может быть), с другой — инвалидировать сущность (FooRequestWasInvalidated). Если тупо выкинем exception, то до инвалидации не дойдет и будет проблема с безопасностью.У тебя бывали кейсы когда действительно несколько причин для отказа?
Здесь еще есть такое преимущество: я могу выкидывать исключение, есть нет соответствующего замыкания с нужным событием/ошибкой: это позволит иметь гарантию того, что при добавлении нового события я добавлю обработку везде. В случае с исключениями можно где-то прое#ать try .. catch и получить проблему, например, ложным 400 Bad Request по FooBusinessException, как описывал в самом начале.P.S. уже в который раз замечаю в твоих примерах функциональный подход с парсингом мета-инфы о параметрах функции. Выглядит красиво. Но перевернуть свое мышление и начать применять у себя... что-то как-то не представляю. Надо попробовать видимо, работу с рефлексией у вас там делает какая-то отдельная внутренняя мини-либа...
Разница в том, что PhpStorm \LogicException и \RuntimeException по умолчанию считает unchecked-исключениями и не требует `@throws` или `try .. catch`Ну какая разница как это называть.
Есть UI и он не должен позволять производить неправильные действия. Например ту же оплату отмененного заказа. Но есть же и публичные API. И там надо корректно отрабатывать все эти ошибки. Так что не совсем LogicException.
Представь, что в PHP появился скалярный тип string<1,..> («строка ненулевой длины»). Наверное, она будет выкидывать TypeError или типа того при пустой строке. Ты будешь ловить TypeError? Или не будешь использовать такой синтаксис? Мне кажется, что assertion'ы можно рассматривать как расширенные type hinting, если хочешь: не нужно это ловить, до этого просто не нужно доводить.class CantPublishPostWithEmptyBody extends BusinessException;