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

Adelf

Administrator
Команда форума
Да сказали же, что пример не очень удачный. Меня больше удивляет вся эта чехарда с PUT/POST запросами, когда каждый адепт по-своему придумывает правила.
Берем банальнейший пример, который идеально показывает провал идеологии rest, когда разные люди пытаются проектировать REST API.
Ban user command.
Как я себе представляю наиболее православный и одобренный богами REST способ:
PUT /users/{id}/banstatus - и тут уже передаем true или false. Но мне даже такой не нравится. Тут в зависимости от аргумента будут сгенерены разные эвенты(UserWasBlocked and UserWasUnblocked) это сродни функции с булевым аргументом(ban(bool $banned)), которую надо отрефакторить, разделив на две, для более удобного интерфейса. А разделив этот REST-ресурс на два, уже получим стандартный rpc.

Я немного погуглил. И на первой странице гугла по rest api ban user запросу такой разброс и шатание.

https://my.axerosolutions.com/spaces/5/communifire-documentation/wiki/view/5619/rest-api-ban-user - первое что у меня вылезло. Чистейший rpc под видом rest.

https://community.jivesoftware.com/thread/279984
  • Identify the user you want to do this to, and end up with a GET /api/core/v3/people/xxx to retrieve that person. Be sure you use fields=@all on whatever this final query is, so that a complete replace does what you expect.
  • Set the jive.enabled field to false, and do a PUT /api/core/v3/people/xxx to deactivate the person.
Ну тут понятно. Чушь с прямым апдейтом свойства. Сиди потом на бекэнде и разбирайся, чтобы сгенерить правильные эвенты

Я еще недавно натыкался на PUT /users/{id}/status - значение нового статуса. А статусов было несколько(normal/banned/blocked/moderated - чот такое)! И там была специальная таблица что произойдет, когда нынешний статус такой то а новый такой то :)) это было очень весело

А что меня больше всего порадовало, так это wrapper для REST API одного.
https://github.com/gnello/php-openfire-restapi#users
Тут сразу видно, что люди не хотят рест. Они хотят просто команд человечно-названных.
PHP:
//Delete a user
$result = $api->Users()->deleteUser('Username');
//Ban a user
$result = $api->Users()->lockoutUser('Username');
//Unban a user
$result = $api->Users()->unlockUser('Username');
И я не понимаю зачем люди пытаются впихнуть человечный naming в нечеловечный гораздо более бедный HTTP-naming.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
в 95% это действительно одна и та же модель.
это, как правило, действительно разные контексты
Но ты заявил, что сам пользователь должен быть представлен в разных контекстах по-разному.
по сути, я перефразировал Фаулера: "Пользователь в админке и пользователь на сайте - это разные сущности потому что они в разных DOM-контекстах."
потому что
it gets progressively harder to build a single unified model. A single conceptual model is a tricky beast to work with.
и следствие:
there is no longer a need for a single conceptual model. The models need not cover everything that can be represented
https://martinfowler.com/bliki/MultipleCanonicalModels.html

это же выглядит со стороны просто нелепо.
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
@Adelf ответ где-то в психологии. Им так сказали (которые в свою очередь сами у кого-то услышали, но из-за эффекта испорченного телефона, приправленного эффектом Даннинга-Крюгера, они готовы делиться своей «мудростью»), они поверили. Времени на то, чтобы это оценить с рациональной точки зрения у них нет в той же мере, как у детей нет времени на то, чтобы оценивать рационально то, что говорит родитель. Родитель так говорит — значит нужно послушаться, это наиболее выигрышная стратегия. Если проводить аналогию с программированием, то люди до сих пор бы изучали теорию, если бы не послушались и не начали писать хоть какой-то код. Говнокод лучше отсутствующего кода.
 

Вурдалак

Продвинутый новичок
по сути, я перефразировал Фаулера: "Пользователь в админке и пользователь на сайте - это разные сущности потому что они в разных DOM-контекстах."
Только вот неувязочка: @Adelf говорил про удаление пользователей сайта (самых обычных, не админов которые) двумя разными способами (ClubMember::deleteByAdmin, ClubMember::leaveClubForAWhile), а не про удаление админов и пользователей (ClubAdmin::delete, ClubMember::delete). Нет, серьёзно, чувак, если это такой жирный троллинг, то он скучен.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
чехарда с PUT/POST запросами, когда каждый адепт по-своему придумывает правила.
Берем банальнейший пример, который идеально показывает провал идеологии rest, когда разные люди пытаются проектировать REST API.
Ban user command.
Как я себе представляю наиболее православный и одобренный богами REST способ:
PUT /users/{id}/banstatus - и тут уже передаем true или false.
Проблема в том, что вы хотите REST, который как RPC, только с методами в URL.
Когда начинаются отглагольные существительные - это не REST.
Не должно быть banstatus. Есть сущность, у нее есть статус. /users/{id}/status
У нее может быть ссылка на словарь возможных значений статуса.

в зависимости от аргумента будут сгенерены разные эвенты(UserWasBlocked and UserWasUnblocked) это сродни функции с булевым аргументом(ban(bool $banned)), которую надо отрефакторить, разделив на две, для более удобного интерфейса. А разделив этот REST-ресурс на два, уже получим стандартный rpc.
Да, ты хочешь RPC. В REST событий нет. Никаких уведомлений или изменений статуса не бывает. Есть только статичное представление, актуальность которого никто не гарантирует.

Я еще недавно натыкался на PUT /users/{id}/status - значение нового статуса. А статусов было несколько(normal/banned/blocked/moderated - чот такое)! И там была специальная таблица что произойдет, когда нынешний статус такой то а новый такой то :)) это было очень весело
Это нормально. Не факт, что удобно в том случае, но именно это и правильно.

Тут сразу видно, что люди не хотят рест. Они хотят просто команд человечно-названных. Delete a user
Скажи, это сеанс психоанализа тут пошел? Я могу про API, а про людей - не готов.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
удаление пользователей сайта (самых обычных, не админов которые) двумя разными способами (ClubMember::deleteByAdmin, ClubMember::leaveClubForAWhile), а не про удаление админов и пользователей (ClubAdmin::delete, ClubMember::delete)
Пользователя (одна запись в базе - то, что Фаулер называет polysemy, рус. "множественность") Админами И Пользователями. Две группы, два контекста, две модели. Как у Queen в one vision, только две.
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Админами И Пользователями. Две группы, два контекста, две модели. Как у Queen в one vision, только две.
Иметь в контексте членов клуба слово «admin» (в имени метода «deleteByAdmin») совершенно не возбраняется. Это слово «admin» чисто формально в рамках контекста «ClubMembers» не имеет никакого отношения к тому понятию «Admin» из контекста «Admins». Связь существует на более высоком уровне — на карте соответствий контекстов (aka context mapping).

Назови это не «удалить админом», а «заблокировать от имени руководства», чо, если тебя это путает. :)
 

Adelf

Administrator
Команда форума
Не факт, что удобно в том случае, но именно это и правильно.
Религиозные догмы пошли в ход. Богоугодное только должен делать ты, ибо иной путь есть ересь и выкинута должна быть из головы.
Status может быть совсем разным. И эта таблица весьма быстро, квадратично растет с увеличением количества статусов. А уж логика на сервере...
Неудивительно, что
Вообще, REST сложнее в реализации
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
да я как-бы не агитирую за REST, он реально сложнее, я лишь за понимание что оно такое, и где он не нужен

то есть, если тебе нужен статус, действие, уведомление, консистентность, транзакции, и все такое - это не к REST

а вот история курса акций, разместить приказ на бирже, добавить виртуалку в кластер - это по REST удобно
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Назови это не «удалить админом», а «заблокировать от имени руководства», чо, если тебя это путает. :)
Я более понятный пример приведу, потому что чувствую, что ты не троллишь, а реально пытаешься разобраться. Замени этих «пользователей» на более интересный контекст «Что? Где? Когда?». Игрок может сам выйти из игры, а его может удалить ведущий игры. Так вот, оба этих действия более чем в bounded context «Что? Где? Когда?», но на уровне веб-сайта под «ведущим» на самом деле может скрываться «администратор сайта», это понятие из другого контекста, но через карту контекстов мы делаем соответствие между этими двумя персонажами. Поэтому мы ни разу не будем вышвыривать игрока в контексте админов: контекст админов вообще ни про какие игры не знает. У тебя просто глаза замылены из-за пересечения терминов, о чём, собственно, и пишет Фаулер, на которого ты так рьяно ссылаешься.
 

Adelf

Administrator
Команда форума
то есть, если тебе нужен статус, действие, уведомление, консистентность, транзакции, и все такое - это не к REST

а вот история курса акций, разместить приказ на бирже, добавить виртуалку в кластер - это по REST удобно
ну мы как раз про это. мы больше про приложения с доменом каким-никаким. Люди привыкают к REST на тупых моделях, которые напрямую мапятся на строчки в бд или виртуалки. А потом тащут его в rich модели. И там пытаются разобраться какая догма верная. Я приводил примеры из гугла не для того чтобы посмеяться. Показал, что народ банально не может понять как надо и делает как получится. Ибо противоестественный этот рест.
 

Вурдалак

Продвинутый новичок
Я повторю и расширю свою мысль на тему REST vs RPC а-ля JSON RPC.
Хороший REST будет совершенно такой же, как JSON RPC в том смысле, что никто нам не мешает делать красивые и говорящие команды прямо в URL.
Здесь просто другой формат записи этих команд.
Здесь есть инфраструктурные отличия: если команда зашита в URL, то проще на уровне того же nginx будет, например, частично убрать трафик какого-то сервиса с одного сервера на другой.
В остальном — это вопрос предпочтений.

REST просто провоцирует новичков на менее обдуманные имена, поскольку вот тут рядом идут CRUD-глаголы от HTTP, которые якобы упрощают жизнь. Но по факту это ловушка. Вот если бы глагола было два — GET (query) и POST (command/mutation), то я уверен, что ситуация была бы куда лучше. Поэтому у меня складывается впечатление, что тут отчасти виноват HTTP :)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Есть такая шутка: любой REST рано или поздно становится RPC.

Отсекать или шейпить часть трафика на уровне роутера с nginx - это очень важная фича в enterprise.
 

fixxxer

К.О.
Партнер клуба
если бы глагола было два — GET (query) и POST (command/mutation)
Да так всю жизнь все и делали, пока не прибежали идеологи со своими put и прочими patch. Причем про смысл post и put могут спорить до посинения, а то, что у них часть глагола оказывается в URI, старательно не замечают :)
 

fixxxer

К.О.
Партнер клуба
Я еще недавно натыкался на PUT /users/{id}/status - значение нового статуса. А статусов было несколько(normal/banned/blocked/moderated - чот такое)! И там была специальная таблица что произойдет, когда нынешний статус такой то а новый такой то :)) это было очень весело
Это нормально. Не факт, что удобно в том случае, но именно это и правильно.
Если это тупая синхронизация статусов, и никаких событий это не порождает, то нормально. (Но часто ли так? По моему опыту - почти никогда.)
А иначе все удобство REST заканчивается на safe methods, дальше начинается создание самому себе проблем по религиозным соображениям.
 

Вурдалак

Продвинутый новичок
Я еще недавно натыкался на PUT /users/{id}/status - значение нового статуса. А статусов было несколько(normal/banned/blocked/moderated - чот такое)! И там была специальная таблица что произойдет, когда нынешний статус такой то а новый такой то :)) это было очень весело
... потом они осознают, что статусы типа «забанен» и «промодерирован» вообще-то независимые и статус промодерированности от факта блокировки меняться не должен. Но будет поздно :)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
С RPC можно говнокодить намного дольше. В REST поля "забанен" и "промодерирован" должны быть разными полями.

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

Adelf

Administrator
Команда форума

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@Adelf, есть разница между неудачным дизайном и ошибкой. Аналогично, у тебя в базе может быть статус - поле типа enum, а может быть несколько полей типа bool.
banstatus как действие - это ошибка, а поле типа enum - просто негибкий дизайн
 

Вурдалак

Продвинутый новичок
Проблема в том, что вы хотите REST, который как RPC, только с методами в URL.
POST /users/42/ban — это абсолютно нормально, об это я и говорил:
никто нам не мешает делать красивые и говорящие команды прямо в URL
И пример уже показывал:
Код:
<account>
   <account_number>12345</account_number>
   <balance currency="usd">100.00</balance>
   <link rel="deposit" href="https://bank.example.com/accounts/12345/deposit" />
   <link rel="withdraw" href="https://bank.example.com/accounts/12345/withdraw" /> 
   <link rel="transfer" href="https://bank.example.com/accounts/12345/transfer" />
   <link rel="close" href="https://bank.example.com/accounts/12345/close" />
</account>
deposit, withdraw, transfer, etc.
 
Сверху