Як API REST підходить для домену на основі команд / дій?


24

У цій статті автор стверджує, що

Іноді потрібно виявити операцію в API, яка по суті не є RESTful.

і це

Якщо API має занадто багато дій, то це свідчить про те, що або він був розроблений з точки зору RPC, а не з використанням принципів RESTful, або що відповідний API, природно, краще підходить для моделі типу RPC.

Це відображає те, що я прочитав і почув і в інших місцях.

Однак я вважаю це досить заплутаним і хотів би краще зрозуміти цю справу.

Приклад I: Вимкнення VM через інтерфейс REST

Я думаю, є два принципово різні способи моделювання відключення VM. Кожен спосіб може мати кілька варіацій, але давайте зосередимося на найбільш фундаментальних відмінностях на даний момент.

1. Виправити державну власність ресурсу

PATCH /api/virtualmachines/42
Content-Type:application/json  

{ "state": "shutting down" }

(Крім того, PUTна субресурсі /api/virtualmachines/42/state.)

ВМ буде вимикатися у фоновому режимі, і в якийсь пізній момент часу, залежно від моменту, вимкнення буде успішним, або стан може бути внутрішньо оновлений з "вимкненням живлення".

2. PUT або POST у властивості дій ресурсу

PUT /api/virtualmachines/42/actions
Content-Type:application/json  

{ "type": "shutdown" }

Результат точно такий же, як у першому прикладі. Стан буде оновлено до "вимкнення" негайно і, можливо, врешті до "вимкнення живлення".

Чи обидва дизайни RESTful?

Який дизайн краще?

Приклад II: CQRS

Що робити, якщо у нас є домен CQRS з багатьма такими "діями" (ака командами), які потенційно можуть призвести до оновлення декількох агрегатів або не можуть бути відображені в операціях CRUD на конкретних ресурсах і субресурсах?

Чи слід намагатися моделювати стільки команд, скільки конкретних створює або оновлює конкретні ресурси, де це можливо (дотримуючись першого підходу з прикладу I) і використовувати "кінцеві точки дії" для решти?

Або ми повинні зіставити всі команди на кінцеві точки дії (як у другому підході в прикладі I)?

Де нам провести лінію? Коли дизайн стає менш ВІДКРИТИМ?

Чи краще модель CQRS підходить для RPC типу API?

Згідно з цитованим вище текстом, це так, як я його розумію.

Як видно з моїх численних запитань, я трохи розгублений у цій темі. Чи можете ви допомогти мені краще зрозуміти це?


"створення дії" не здається RESTful, за винятком випадків, коли виконана дія має власний ідентифікатор ресурсу. В іншому випадку зміна властивості "state" через PATCH або PUT має більше сенсу. Що стосується частини CQRS, я ще не маю гарної відповіді.
Фабіан Шменглер

3
@Laiv У цьому нічого поганого. Це академічне питання, я хотів би глибше зрозуміти цю справу.
leifbattermann

Відповіді:


19

У першому випадку (відключення віртуальних машин) я вважаю, що жодна з альтернативних варіантів ОП не є доброю. Звичайно, якщо ви використовуєте модель зрілості Річардсона як мірку, вони обидва рівні 2 API, тому що вони використовують ресурси та дієслова.

Хоча жодна з них не використовує гіпермедіа-елементи контролю, і, на мою думку, це єдиний тип REST, який відрізняє дизайн RESTful API від RPC. Іншими словами, дотримуйтесь рівнів 1 і 2, і ви матимете API у стилі RPC у більшості випадків.

Для того, щоб змоделювати два різних способи відключення VM, я б викрив сам VM як ресурс, який (серед іншого) містить посилання:

{
    "links": [{
        "rel": "shut-down",
        "href": "/vms/1234/fdaIX"
    }, {
        "rel": "power-off",
        "href": "/vms/1234/CHTY91"
    }],
    "name": "Ploeh",
    "started": "2016-08-21T12:34:23Z"
}

Якщо клієнт бажає вимкнути PloehVM, він повинен перейти за посиланням на shut-downтип відносин. (Зазвичай, як зазначено в « Готовій книзі RESTful Web Services» , ви використовуєте IRI або більш детальну схему ідентифікації для типів відносин, але я вирішив зберегти приклад максимально простим.)

У цьому випадку мало іншої інформації для надання дії, тому клієнт повинен просто зробити порожній POST проти URL-адреси в href:

POST /vms/1234/fdaIX HTTP/1.1

(Оскільки цей запит не має тіла, було б заманливо моделювати це як GET-запит, але GET-запити не повинні мати помітних побічних ефектів, тому POST є більш правильним.)

Так само, якщо клієнт хоче вимкнути VM, він power-offзамість цього перейде за посиланням.

Іншими словами, типи зв’язків посилань надають переваги, які вказують на наміри. Кожен тип відносин має специфічне смислове значення. Саме тому ми іноді говоримо про семантичну павутину .

Щоб зберегти приклад максимально зрозумілим, я навмисно затемнював URL-адреси в кожному посиланні. Коли сервер хостингу отримує вхідний запит, він знатиме, що це fdaIXозначає вимкнення і CHTY91означає вимкнення живлення .

Як правило, я б просто закодувати дію в самому URL, так що URL - б /vms/1234/shut-downі /vms/1234/power-off, але коли вчення, що стирає відмінності між типами відносин (семантики) і URL - адрес (деталі реалізації).

Залежно від того, які у вас є клієнти, ви можете розглянути можливість використання RESTful URL-адрес, які не можна зламати .

CQRS

Що стосується CQRS, одна з небагатьох речей, з якою погоджуються Грег Янг та Уді Дахан, - це те, що CQRS не є архітектурою вищого рівня . Таким чином, я з обережністю став би зробити RESTful API занадто схожим на CQRS, оскільки це означатиме, що клієнти стають частиною вашої архітектури.

Часто рушійною силою реального API RESTful (рівень 3) є те, що ви хочете мати можливість розвивати свій API, не порушуючи клієнтів і не контролюючи клієнтів. Якщо це ваша мотивація, то CQRS не був би моїм першим вибором.


Ви маєте на увазі, що обидва перші приклади не є ВІДКРИТИМИ, оскільки вони не використовують гіпермедіа контроль? Але я навіть не публікував жодних відповідей, лише URL-адреси та органи запиту.
leifbattermann

4
@leifbattermann Вони НЕ ВІДПОВІДНІ, оскільки вони використовують тіло повідомлення для спілкування про наміри; це явно RPC. Якщо ви використовували посилання для отримання цих ресурсів, то навіщо вам потрібно спілкуватись про наміри через тіло?
Марк Семанн

Що має сенс. Чому ви пропонуєте POST? Чи не є дія самодіяльною? У будь-якому випадку, як сказати своєму клієнту, який метод HTTP використовувати?
leifbattermann

2
@ guillaume31 DELETEмені здається дивним, оскільки після вимкнення vm все ще буде існувати, лише в стані "вимкнення живлення" (або що-небудь подібне).
leifbattermann

1
Ресурс не повинен відображати VM атемпорально, він може представляти екземпляр його виконання.
guillaume31

6

Вимкнення VM через інтерфейс REST

Це насправді дещо відомий приклад, викладений Тімом Брей у 2009 році .

Рой Філдінг, обговорюючи проблему, поділився цим спостереженням :

Я особисто віддаю перевагу системам, які розглядають моніторинг стану (наприклад, стан живлення) як нередагований.

Коротше кажучи, у вас є один інформаційний ресурс, який повертає поточне подання стану, що контролюється; це представлення може містити посилання гіпермедіа до форми, яка вимагає зміни до цього стану, а форма має ще одне посилання на ресурс для обробки (кожного) запиту на зміну.

Сет Ладд мав ключову думку про проблему

Ми перетворили Бег від простого стану людини до справжнього Іменника, про який можна створити, оновити та поговорити.

Повернення цього до перезавантаження машин. Я заперечую, що ви POST на / vdc / 434 / cluster / 4894 / server / 4343 / перезавантажуєтесь Після публікації у вас є URI, який представляє цю перезавантаження, і ви можете отримати її для оновлення статусу. Через магію гіперпосилання представлення Перезавантаження пов'язане із Сервером, який перезавантажується.

Я думаю, що карбування простору URI дешево, а URI - навіть дешевше. Створіть колекцію видів діяльності за зразком іменників та POST, PUT та DELETE away!

RESTful програмування - це бюрократія Vogon у веб-масштабі. Як ти робиш що- небудь ВІДНОМО? Винайдіть нові документи для цього та оцифруйте документи.

Дещо вигадливішою мовою, те, що ви робите, - це визначення протоколу додатка домену для "вимкнення VM" та визначення ресурсів, які потрібно виставити / реалізувати цей протокол

Дивлячись на власні приклади

PATCH /api/virtualmachines/42
Content-Type:application/json  

{ "state": "shutting down" }

Все добре; ви насправді не розглядаєте сам запит як власний окремий інформаційний ресурс, але ви все одно можете керувати.

Ви трохи пропустили своє уявлення про зміну.

Однак із PATCH додане об'єднання містить набір інструкцій, що описують, як ресурс, що перебуває в даний час на початковому сервері, повинен бути модифікований для створення нової версії.

Наприклад, формат носіїв формату JSON Patch форматує інструкції так, ніби ви безпосередньо змінювали документ JSON

[
    { "op": "replace", "path": "state", "value": "shutting down" }
]

У Вашій альтернативи ідея близька, але не очевидно правильна. PUT- це повна заміна стану ресурсу за цільовою URL-адресою , тому ви, ймовірно, не вибрали б написання, схоже на колекцію, як ціль представлення одного об'єкта.

POST /api/virtualmachines/42/actions

Відповідає вигадці про те, що ми додаємо дію до черги

PUT /api/virtualmachines/42/latestAction

Відповідає вигадці про те, що ми робимо оновлення до хвостового елемента в черзі; трохи дивно це робити так. Принцип найменшого сюрпризу рекомендує надавати кожному PUT свій унікальний ідентифікатор, а не розміщувати їх у одному місці та змінювати кілька ресурсів одночасно.

Зауважте, що, оскільки ми обговорюємо написання URI - REST не хвилює; /cc719e3a-c772-48ee-b0e6-09b4e7abbf8bє ідеально кромулентним URI, що стосується REST. Читання, як і імена змінних, є окремим питанням. Використання написань, які відповідають RFC 3986 , зробить людей набагато щасливішими.

CQRS

Що робити, якщо у нас є домен CQRS з багатьма такими "діями" (ака командами), які потенційно можуть призвести до оновлення декількох агрегатів або не можуть бути відображені в операціях CRUD на конкретних ресурсах і субресурсах?

Грег Янг на CQRS

CQRS - це дуже простий шаблон, який дає багато можливостей для архітектури, які в іншому випадку можуть не існувати. CQRS не є можливою послідовністю, він не відбувається, це не обмін повідомленнями, він не має відокремлених моделей для читання і запису, а також не використовує пошук подій.

Коли більшість людей говорять про CQRS, вони дійсно говорять про застосування шаблону CQRS до об'єкта, який представляє службову межу програми.

Зважаючи на те, що ви говорите про CQRS в контексті HTTP / REST, здається, розумно припустити, що ви працюєте в цьому останньому контексті, тому давайте продовжимо це.

Це, на диво, навіть простіше, ніж ваш попередній приклад. Причина цього проста: команди - це повідомлення .

Джим Вебер описує HTTP як протокол програми офісу 1950-х років; робота виконується за допомогою прийому повідомлень та розміщення їх у поштових скриньках. Така ж ідея - ми отримуємо чисту копію форми, заповнюємо її специфікою, яку ми знаємо, доставляємо. Та да

Чи слід намагатися моделювати стільки команд, скільки конкретних створює або оновлює конкретні ресурси, де це можливо (дотримуючись першого підходу з прикладу I) і використовувати "кінцеві точки дії" для решти?

Так, якщо "конкретними ресурсами" є повідомлення, а не сутності в доменній моделі.

Ключова ідея: ваш REST API все ще є інтерфейсом ; ви повинні мати можливість змінити базову модель, не вимагаючи від клієнтів змінити повідомлення. Коли ви випускаєте нову модель, ви випускаєте нову версію веб-кінцевих точок, які знають, як прийняти протокол домену та застосувати його до нової моделі.

Чи краще модель CQRS підходить для RPC типу API?

Не насправді - зокрема, веб-кеші - чудовий приклад "зрештою послідовної моделі читання". Зробити кожен із ваших поглядів незалежно адресованим, кожен зі своїми правилами кешування, дає вам безліч масштабів безкоштовно. Існує відносно мало апеляцій до виключно підходу RPC до читання.

Для записів це складніше питання: надіслати всі команди одному обробнику в одній кінцевій точці або одному сімейству кінцевих точок, звичайно, простіше . REST - це справді більше про те, як ви знаходите спілкування, де кінцева точка знаходиться з клієнтом.

Поводження з повідомленням як власним унікальним ресурсом має перевагу в тому, що ви можете використовувати PUT, попереджаючи посередницькі компоненти про те, що обробка повідомлення є безсилою, щоб вони могли брати участь у певних випадках помилок, приємно мати . (Зауважте: що з точки зору клієнтів, якщо ресурси мають різний URI, то це різні ресурси; той факт, що вони можуть мати однаковий код обробника запитів на сервері походження, є деталлю реалізації, прихованою уніформою інтерфейс).

Філдінг (2008)

Я також повинен зазначити, що вищевикладене ще не є повністю ВІДКРИТИм, принаймні як я використовую цей термін. Все, що я зробив, - це описані сервісні інтерфейси, які не більше ніж будь-які RPC. Для того, щоб зробити його RESTful, мені потрібно було б додати гіпертекст, щоб запровадити та визначити послугу, описати, як виконати відображення за допомогою форм та / або шаблонів посилань, та надати код для комбінування візуалізацій корисними способами.


2

Ви можете використовувати 5 рівнів типу медіа, щоб вказати команду в полі заголовка типу вмісту запиту.

У прикладі VM це було б щось у цьому напрямку

PUT /api/virtualmachines/42
Content-Type:application/json;domain-model=PowerOnVm

> HTTP/1.1 201 Created
Location: /api/virtualmachines/42/instance

Потім

DELETE /api/virtualmachines/42/instance
Content-Type:application/json;domain-model=ShutDownVm

Або

DELETE /api/virtualmachines/42/instance
Content-Type:application/json;domain-model=PowerOffVm

Дивіться https://www.infoq.com/articles/rest-api-on-cqrs


Зауважте, 5LMT було запропонованим рішенням і не підтримується стандартами . Я раніше натрапив на статтю CQRS і багато чого навчився з неї.
Петро Л

1

Приклад у пов’язаній статті базується на ідеї, що запуск машини та її вимкнення має спрямовуватися командами, а не змінами стану модельованих ресурсів. Останнє - це те, що REST живе і дихає. Для кращого моделювання ВМ потрібен погляд на те, як працює його аналог у реальному світі та як ви, як людина, взаємодієте з ним. Це довгодухо, але я думаю, що це добре розуміє тип мислення, необхідний для гарного моделювання.

Є два способи контролю стану живлення комп’ютера на моєму столі:

  • Вимикач живлення: негайно припиняє подачу електроенергії до джерела живлення, приводячи весь комп'ютер до раптового, безладного зупинки.
  • Кнопка включення / вимкнення: Показує обладнання, щоб повідомити програмне забезпечення, що щось із зовнішньої сторони хоче, щоб усе було вимкнено. Програмне забезпечення здійснює впорядковане відключення, сповіщає обладнання про його завершення, і апаратне обладнання сигналізує про джерело живлення, що може перейти в стан очікування. Якщо вимикач живлення увімкнено, машина працює, а програмне забезпечення знаходиться в стані, коли воно не може реагувати на сигнал відключення, система не вимкнеться, якщо не вимкнути вимикач живлення. (ВМ буде поводитись точно так само; якщо програмне забезпечення ігнорує сигнал відключення, машина продовжить працювати. Я повинен змусити її вимкнути живлення.) Якщо я хочу мати змогу перезапустити машину, мені доведеться увімкніть вимикач живлення та натисніть кнопку включення / вимкнення. (Багато комп'ютерів мають можливість довгого натискання кнопки живлення, щоб перейти безпосередньо в режим очікування, але ця модель насправді цього не потребує.) Ця кнопка може розглядатися як перемикач, оскільки натискання на неї призводить до різної поведінки залежно від стану при натисканні. Якщо вимикач живлення вимкнено, натискання цієї кнопки не робить абсолютно нічого.

Для VM обидва ці моделі можуть бути змодельовані як читання / запис булевих значень:

  • power- Якщо зміниться на true, нічого не відбувається, окрім повідомлення про те, що перемикач переведений у цей стан. При зміні на falseВМ передається режим негайного відключення живлення. Заради повноти, якщо значення не змінюється після запису, нічого не відбувається.

  • onoff- Якщо буде змінено на true, нічого не відбувається, якщо powerє false, інакше команда VM запускається. Якщо змінити на false, нічого не станеться, якщо powerв falseіншому випадку команда VM отримує сповіщення програмного забезпечення про впорядковане вимкнення, яке воно зробить, а потім сповіщення ВМ про те, що він може перейти в стан вимкнення. Знову ж таки, для повноти запис без змін нічого не робить.

При всьому цьому випливає розуміння, що існує одна ситуація, коли стан машини не відображає стан комутаторів, і це відбувається під час вимкнення. powerвсе ще буде trueі onoffбуде false, але процесор все ще працює на його відключенні, і для цього нам потрібно додати ще один ресурс, щоб ми могли сказати, що машина насправді робить:

  • running- Значення лише для читання, trueколи VM працює і falseколи його немає, визначається, запитуючи гіпервізор про його стан.

Підсумок цього полягає в тому, що якщо ви хочете запускати VM, ви повинні переконатися, що ресурси powerта onoffресурси були встановлені true. (Ви можете дозволити powerпропустити крок, зробивши його самостійним скиданням, так що якщо він встановлений false, він стає trueпісля того, як VM буде перевірено жорстко зупиненим. Чи то це RESTfully-чиста річ, щоб зробити, це корм для іншої дискусії.) якщо ви хочете, щоб це зробити нормальне завершення роботи, ви встановили onoffв falseі повернутися пізніше , щоб побачити , якщо машина фактично зупинилася, установка powerдля falseякщо це не так.

Як і в реальному світі, у вас все ще виникає проблема, щоб вас направили на запуск VM після того, як він onoffзмінився, falseале все ще є runningтому, що він знаходиться в середині відключення. Як ви з цим справляєтесь - це політичне рішення.


0

Чи обидва дизайни RESTful?

Тож якщо ви хочете подумати спокійно, тоді забудьте про команди. Клієнт не каже серверу відключити VM. Клієнт "вимикає" (метафорично кажучи) свою копію представлення ресурсу, оновивши його стан, а потім виводить це представлення на сервер. Сервер приймає, що нове представлення стану і як побічний ефект цього фактично вимикає VM. Аспект побічної дії залишається на сервері.

Це менше

Ей сервер, клієнт тут, ви б не хотіли вимкнути VM

і більше

Привіт сервер, клієнт тут, я оновив стан ресурсу VM 42 на стан вимкнення, оновив вашу копію цього ресурсу, а потім зробіть все, що ви вважаєте за потрібне

Як побічний ефект від прийняття цього нового стану сервера, він може перевірити, які дії він повинен насправді виконувати (наприклад, фізичне вимкнення VM 42), але це прозоро для клієнта. Клієнт не переймається будь-якими діями, які повинен вжити сервер, щоб стати узгодженим з цим новим станом

Тож забудьте про команди. Єдині команди - це дієслова HTTP для передачі стану. Клієнт не знає, і це не хвилює, як сервер збирається перевести фізичний VM у стан вимкнення. Клієнт не видає команди серверу для досягнення цього, він просто говорить Це новий стан, зрозумійте це .

Сила цього полягає в тому, що він від’єднує клієнта від сервера з точки зору контролю потоку. Якщо пізніше сервер змінить, як він працює з VM, клієнту все одно. Кінцевих точок команд для оновлення немає. У RPC, якщо ви зміните кінцеву точку API shutdownна, shut-downви зламали всіх своїх клієнтів, оскільки вони тепер не знають команди зателефонувати на сервер.

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


Thx для вашої відповіді. Друга частина про роз'єднання клієнта та сервера дуже узгоджується з моїм власним розумінням. Чи є у вас ресурс / посилання, що створює резервну копію першої частини вашої відповіді? Яке саме обмеження REST порушується, якщо я використовую ресурси, методи HTTP, гіпермедіа, самоописові повідомлення тощо?
leifbattermann

Немає проблем із використанням ресурсів, методів HTTP тощо. Зрештою, HTTP є протоколом RESTful. Якщо проблема виникає, використовуйте те, що ви називаєте "кінцеві точки дії". У REST є ресурси, які представляють поняття або речі (наприклад, віртуальна машина 42 або мій банківський рахунок), а дієслова HTTP використовують для передачі стану цих ресурсів між клієнтами та серверами. Це все. Що ви не повинні робити, це спробувати створити нові команди, комбінуючи нересурсні кінцеві точки з HTTP-дієсловами. Отже, "virtualmachines / 42 / Actions" не є ресурсом і не повинен існувати в системі RESTful.
Кормак Малхолл

Або кажучи інакше, клієнт не повинен намагатися виконувати команди на сервері (за винятком обмежених дієслів HTTP, що стосуються виключно стану передачі ресурсів). Клієнт повинен оновити свою копію ресурсу, а потім просто попросити сервер прийняти цей новий стан. Прийняття цього нового стану може мати побічні ефекти (VM 42 фізично вимкнено), але це не стосується клієнта. Якщо клієнт не намагається запускати команди на сервері, то між цими командами між клієнтом і сервером немає з'єднання.
Кормак Малхолл

Ви можете запустити команду на ресурсі ... Як би ви зробили, скажімо, "депозит" та "зняття" на банківському рахунку? Це використовує CRUD для чогось, що не є CRUD.
Конрад

Краще використовувати POST /api/virtualmachines/42/shutdownзамість того, щоб мати якийсь «побічний ефект». API повинен бути зрозумілий користувачеві, як я можу знати, що, наприклад DELETE /api/virtualmachines/42, вимкне VM? Побічним ефектом для мене є помилка, ми повинні розробити наші API, щоб вони були зрозумілими та самоописовими
Konrad
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.