Что за мода пошла? REST-like HTML CRUD

Вурдалак

Продвинутый новичок
а как на счет атомарности?
Каждая команда атомарна. Если у тебя в принципе нет никаких событий, привязанных на отдельно взятое действие типа rename, relocate, etc., то можно, в принципе, сделать это одной командой. Как я выше сказал, тут могут быть команды ChangeProfile { userId, name, location, ...} и BanUser { userId }. Ты уверен, что эти действия действительно так обязательно выполнять атомарно, только потому что это одна форма? Ты не встречал, например, форм, которые сразу применяют действие, там даже нет кнопки «Сохранить» (в ВК есть такое, по-моему)? Это чисто вопрос UI. Ну, в конце концов, можно обе команды запустить в одной транзакции БД, это будет специальный хак для UI. Но фактически с этим проблем нет: да, мы может обновить профиль, но зафейлим команду бана юзера. Что тут плохого?

ну дело не в пользователе, это же и заказы, деньги?
Можешь привести пример о чём ты говоришь?

Ещё один момент, заставляющий задуматься: твоя форма отправляет большой скоп данных, вполне вероятно, что юзер изменится, пока ты редактируешь его. Тут мог бы помочь какой-нибудь optimistic lock, но повышается риск того, что даже на неконфликтные с точки зрения бизнес-логики действия форма будет сообщать о конфликтах (ты меняешь юзеру имя, а рядом кто-то его забанил).

А если ты не используешь optimistic lock, то у тебя вообще будет код, который будет показывать, что ты, например, разбанил юзера (появляется соответствующее событие, сработают методы компенсации за бан и прочее), когда ты ничего такого не делал: просто в момент загрузки формы у тебя была снята галка, а соседний модератор успел забанить этого пользователя.

Поэтому лучше, когда клиент в явном виде сообщает дифф изменений: набор команд, которые решил предпринять юзер. Явное лучше неявного. Если только мы не занимаемся обфускацией.
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
У меня crud в голом виде также редко встречается, чаще действие, но если вижу подобные формы, мне в голову сразу приходит голый генератор типа крад, ну и рест как оболочка но это так скушно. Пример денежных операций, из сегоднешнего. Форма типа кнопка на продукте, добавить продукт. Теперь мир вокруг: есть бюргеркинг с главным филиалом в берлине, который в итоге получит счет. Он выделил бюджет филиалу, который на эту кнопку нажмет и получит товар ( получатель). этот филиал прописан к оному из складов (поставщик). Немного запутанней, филлиал накидает много товаров в корзину, а заказ один. Бюджет не превышает по группам, (моющие средства/тряпки, салфетки/трубочки). После отправки заказа шаг назад либо сложный, звонки, откаты, либо даже невозможен. Есть дата заказа, есть туры когда транспорт едет, есть выходные и праздники. Это вероятнее всего только часть перечня, о чем нужно подумать. Но в итоге либо заказ, уменьшить бюджет, записать услугу, отправить заказ... либо извените и причины.
 

Вурдалак

Продвинутый новичок
Отправка заказа, списание денег, запись услуги... Я правильно понимаю, что ты эти действия выполняешь в одной транзакции? Это удобно, биллинг лежит в той же БД. Но что если оказание услуги и, например, списание денег будут находиться на разных серверах? Представь, ты оформил заказ, и тут fatal error, не успел списать с удаленного сервера деньги. Что дальше?
 

WMix

герр M:)ller
Партнер клуба
Грубо говоря да, есть конечно разделенные запросы, а некоторые могут находится вне. Но основа от начала до коммита. В контроллере простой вызов, отправить. А дальше в зависимости от ситуации. Конечно рест тут как пришей кабыле хвост, но клиент на js и удобнее сказать put, на сервере инзерт, а вот в моделе начинаем по шагам разбираться. Ну получится тоже самое но из модели. Вот с фатал ничего не произойдет, если только базы разделенные, но там и подход иной. Но и это никаким боком к ресту или краду не относится. Это уже модель, не?
 

Вурдалак

Продвинутый новичок
Возможно в твоём случае вся форма действительно принадлежит одному бизнес-действию. Моя основная мысль была в том, что формы не должны влиять на модель. Формы — это детали интерфейса. Бан может быть как галочкой в одной большой форме, так и отдельной кнопкой. Интерфейсно выглядит по-разному, но бизнес-действие одно: бан пользователя. Когда ты общаешься с менеджерами, тестерами, ты вряд ли говоришь «я поставил галку в форме редактирования пользователя»; ты обычно говоришь «я забанил пользователя», т.е. ты оперируешь бизнес-действиями и их гораздо больше, чем просто добавить/изменить/удалить.

Есть ещё такое понятие, как task based UI как противопоставление CRUD-формам. Даже с точки зрения UX мне кажется, что task based обычно лучше, чем одна гигантская форма.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Все-таки, давайте вернемся к теории. Сначала давайте вспомним, что REST является подмножеством RPC.

Отделим котлеты от мух, и рассматривать протокол будем отдельно, а API моделей - отдельно. Они не связаны. Хочется их связать, и это представлено аксиомой, но объяснить почему никто не сможет потому что связи нет. Да, сейчас будет флейм на тему моей личности, и я его почищу :)

Объединение rpc-запросов в bulk и цепочки - это оптимизация, а не суть протокола.
Атомарность и локи - это свойства логики приложения, не связанные с протоколом. Распределенные транзакции надо рассматривать в контексте 2pc с известным ограничением, и в отдельной теме.

Аналогично, отделяем котлеты от салата. Если при оформлении заказа появляется fatal error, не происходит ничего. У заказа остается неоплаченный статус, уведомление об ошибке во время оплаты попадает администратору, и вопрос решается в ручном режиме.
С протоколом это не связано вообще.

Я много лет использую json-rpc. Одновременно я использую и RESTful.
 

fixxxer

К.О.
Партнер клуба
Так никто же не против использования rest там, где он уместен.
А вот если rest и activerecord поставить во главу угла и логически развить до полного мяу, получится StrongLoop, хипстерская модная тема, аж IBM купил за охуллиард. Антипаттерн на антипаттерне, просто полная жесть. :)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Предполагаю, что причина в автогенерации кода. Простой RESTFul API можно нагенерировать, а RPC надо писать руками.
 

Вурдалак

Продвинутый новичок
Отталкивайся от того, что нас не интересует анемичная модель. Нам нужна модель, которая реально отражает бизнес-действия.

Если ты под автогенерацией ты понимаешь «взять сущность, её свойства и добавить к ним глаголы GET/POST/PUT/DELETE», то очевидно, что с rich model такое сделать невозможно. К тому же получается API низкого качества, который тоже по понятным причинам не отражает бизнес-действий. Про проблемы такого API я уже говорил выше.

REST сам по себе не обозначает, конечно, что запросы применяются напрямую к модели. Существует промежуточный DTO-объект. Но проблема маппинга неявного DTO на явный набор бизнес-действий никуда не исчезает. И я за то, чтобы этим DTO-объектом была команда, а не состояние сущности.

И эта мысль приходит не мне одному в голову:
https://twitter.com/nicolopigna/status/708275214111653888
https://groups.google.com/forum/#!topic/dddinphp/0SIRm90dIFI
В последнем обсуждении пример Mathias'а на самом деле сводится к вырожденному случаю, когда мы по факту имеем а-ля JSON RPC. Я не против, если тот API, который вы называете REST, будет выглядеть именно так: POST /commands/ {"command": ..., "payload": ...}. Но когда говорят про REST обычно имеют в виду совсем другое.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Нам нужна модель, которая реально отражает бизнес-действия.

Если ты под автогенерацией ты понимаешь «взять сущность, её свойства и добавить к ним глаголы GET/POST/PUT/DELETE», то очевидно, что с rich model такое сделать невозможно. К тому же получается API низкого качества, который тоже по понятным причинам не отражает бизнес-действий.
Конечно.
Есть участок задач, которые надо сделать как можно дешевле, и поддерживать их не предполагается. А если и надо будет менять - то через много лет.
MVP, CRUD SEO-полей сайта, редактирование постов в блоге.
У денег есть цена - деньги в будущем гораздо дешевле, чем сегодня. Поэтому автогенерация бывает экономически выгодна.
Это не общее правило, конечно, надо оценивать риски.

проблема маппинга неявного DTO на явный набор бизнес-действий никуда не исчезает. И я за то, чтобы этим DTO-объектом была команда, а не состояние сущности.
И эта мысль приходит не мне одному в голову:
https://twitter.com/nicolopigna/status/708275214111653888
https://groups.google.com/forum/#!topic/dddinphp/0SIRm90dIFI
В последнем обсуждении пример Mathias'а на самом деле сводится к вырожденному случаю, когда мы по факту имеем а-ля JSON RPC. Я не против, если тот API, который вы называете REST, будет выглядеть именно так: POST /commands/ {"command": ..., "payload": ...}. Но когда говорят про REST обычно имеют в виду совсем другое.
REST for CRUD, RPC for commands, я бы сказал.
POST /commands/ {"command": ..., "payload": ...} - это обычный RPC
 

Вурдалак

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

Я просто не раз на форуме слышал про REST, как какой-то стандарт де-факто, как нечто по определению «правильное», я просто хочу развеять этот странный миф. Нужно лишний раз подумать, прежде чем в качестве API выбирать именно REST.

Для появления обсуждения приходится иногда набрасывать, провоцировать, это да.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Забыл я уже про этот старый тренд, а тут @Вурдалак напомнил.
Тема эта была в апреле, а летом по стечению обстоятельств я вник в REST достаточно глубоко. Стандарты некоторых корпораций сейчас требуют реализацию API по REST. Все, что я написал в апреле - ламерство.
Помню, я даже вынес этот вопрос на совещание комитета по архитектуре, и обсудить собралось человек 50.

Для начала, REST - это нифига не протокол. REST - это
архитектурный стиль взаимодействия компонентов распределённого приложения в сети. REST представляет собой согласованный набор ограничений, учитываемых при проектировании распределённой гипермедиа-системы.
Все слова важны. Это правила, которые ограничивают. Это гипермедиа.

Нам надо забанить пользователя. Мы такие телепаты! Откуда ж мы узнали про пользователя, которого уже хотим забанить? Сначала мы получаем данные пользователя. Начинаем с запросов GET. В ответе на этот запрос будут ссылки на все ресурсы, которые нам доступны про этого пользователя.

Манипуляция ресурсами через представление
  • Если клиент хранит представление ресурса, включая метаданные — он обладает достаточной информацией для модификации или удаления ресурса.
Хотим сменить статус. Откуда мы узнали о том, что у пользователя меняется статус? Из документации про API? Нет. В информации о пользователе будет или его статус, или ссылка на другой ресурс, который описывает его статус. Статус может быть отдельной сущностью, и его можно сохранять, если так спроектировать.

Вы ограничены HTTP-глаголами GET, PUT, POST, DELETE
Нифига. REST не ограничен методами HTTP 1.1. Спокойно делаем свой теплый ламповый метод BAN, если так хочется.
Выделяются "HTTP-based RESTful APIs". В них для изменения состояния части ресурса можно использовать HTTP-метод PATCH.

Транзакции. Отправка заказа, списание денег, запись услуги. Это все разные, несвязанные действия. В REST состояния нет. Не нравится - обойдитесь без REST, делайте свой 2PC.

Пример, где REST очень удобен - доступ по API к разным каталогам. В REST API обычно делается постраничная разбивка, и в ответ включают ссылки на другие страницы.
Клиентские модули есть во многих фреймворках, их можно искаропки использовать со всеми API, остается дописать обработку данных.
 
Последнее редактирование:

fixxxer

К.О.
Партнер клуба
@grigori, тут произошла очень частая история, когда изначальный смысл потерялся и все свелось к тупой фигне. Самоадресацию мало кто осилил, да и я бы не сказал, что она часто нужна. Все свелось к примитивной модной штуке с каждому понятным маппингом CRUD на HTTP methods.

Мне это напоминает историю с hungarian notation, когда из изначально здравой простой идеи помечать префиксом "класс" - чтобы отличать деньги от яблок - стали префиксами помечать тип переменной, то есть сематическую информацию (что сложнее и требует думать головой) заменили на техническую (что просто, но тупо), и, да, это стало корпоративным стандартом.
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Транзакции. Отправка заказа, списание денег, запись услуги. Это все разные, несвязанные действия. В REST состояния нет.
Только причём тут состояние?

Пример, где REST очень удобен - доступ по API к разным каталогам. В REST API обычно делается постраничная разбивка, и в ответ включают ссылки на другие страницы.
Мне кажется, каталог как-нибудь вывести можно и без HATEOAS. Да и навигация — это предпочтение клиента, разное количество выводимых ссылок, скролл вместо постраничной навигации и т.д.
На мой взгляд, это выглядит интереснее, когда речь идёт про ограничение списка команд: объявление о продаже можно отредактировать, но только если оно не забанено; опубликовать статью можно, только если она одобрена и т.д. Как в конечном автомате у тебя есть состояние (нет, это не то состояние, о котором написано в Википедии) и список допустимых переходов.
 

WMix

герр M:)ller
Партнер клуба
это не то состояние
это тоже состояние просто перзистентность меж сессионная и актеров несколько
когда речь идёт про ограничение списка команд
crud и rest никаким образом не влияет на домен, и даже в случае обновления полной публичной сущности могут быть правила в приватных полях регулирующие публикование
 

Вурдалак

Продвинутый новичок
это тоже состояние просто перзистентность меж сессионная и актеров несколько
crud и rest никаким образом не влияет на домен, и даже в случае обновления полной публичной сущности могут быть правила в приватных полях регулирующие публикование
Позовите переводчика.
 

WMix

герр M:)ller
Партнер клуба
Позовите переводчика.
перзистентность меж сессионная и актеров несколько
состояние не зависит от сессий, несколько пользователей с различными ролями. (писатель записал, редактор отредактировал, публишер опубликовал -- 3 состояния, 3 актера)
что еще не понятно, самоучка чтоль?
 

Adelf

Administrator
Команда форума
По моему, вы еще на первой странице полгода назад все обсудили. И вроде ваши мнения с тех пор не поменялись. Понимать друг друга перестали только :)
 
Сверху