Пошук подій та REST


17

Я натрапив на дизайн подій Sourcing, і я хотів би використовувати в додатку, де потрібен клієнт REST (RESTful, щоб бути точним). Однак я не можу з'єднати це разом, оскільки REST досить схожий на CRUD, а пошук подій базується на завданнях. Мені було цікаво, як ви можете спроектувати створення команд на основі запитів на REST-сервер. Розглянемо цей приклад:

За допомогою REST ви можете поставити новий стан ресурсу під назвою File. В одному запиті ви можете надіслати нове ім'я файлу, ви можете змінити батьківську папку та / або змінити власника файлу тощо.

Як побудувати сервер, щоб я міг використовувати пошук подій. Я думав про такі можливості:

  1. Визначити на сервері , які поля були змінені , і створити відповідні команди ( RenameFileCommand, MoveFileCommand, ChangeOwnerCommand...) і відправка їх по окремості. Однак у цій налаштуваннях кожна команда може не допустити інших транзакцій і, таким чином, не змінити "атомну" зміну ресурсу.

  2. Відправка лише одна команда ( UpdateFileCommand) і в обробнику команди, точніше , в сукупності, визначають , які поля були змінені і передавати окремі події замість ( FileRenamedEvent, FileMovedEvent, OwnerChangedEvent...)

  3. Цей мені зовсім не подобається: У запиті на сервер я б вказав у заголовках, яку команду використовувати, оскільки інтерфейс все ще заснований на завданнях (але спілкування здійснюється через REST). Однак це не вдасться в будь-якому іншому використанні зв'язку REST (наприклад, у зовнішніх додатках), оскільки вони не зобов'язані змінювати лише одне поле в одному запиті. Також я приношу досить велике з'єднання в інтерфейс, REST та бази даних ES.

Якому б ви віддали перевагу чи є кращий спосіб впоратися з цим?

Бічна примітка: додаток, написане на Java та Axon Framework для пошуку подій.


Звичайно не 3-й. Я зробив би 1-е, але я мушу подумати над цим.
inf3rno

Ви ставите питанням, чи може один запит HTTP спричинити кілька команд? Я добре розумію? Імхо. це має бути в змозі зробити це, але я не читав про DDD деякий час, тому мені доведеться перевірити зразок коду про те, як це здійснити.
inf3rno

Я знайшов приклад, але це CRUD. github.com/szjani/predaddy-issuetracker-sample/blob/3.0/src/hu/… Я запитаю автора, яка його думка, він знає більше про DDD, ніж я.
inf3rno

1
Моє те, що ви повинні використовувати кілька команд в "одиниці роботи", якщо ви хочете негайної послідовності. Якщо ви говорите про можливу послідовність, то питання для мене не має сенсу. Ще одне можливе рішення надіслати CompositeCommand, який може містити команди, які потрібно виконати атомними. Це може бути проста колекція, важливо лише те, що шина може правильно поводитися з нею.
inf3rno

1
За його словами, ви повинні намагатися досягти взаємозв'язку між командами та HTTP-запитами 1: 1. Якщо ви не можете цього зробити (це поганий запах), тоді вам слід використовувати сипучу частину (я назвав її складовою), щоб зробити її атомною.
inf3rno

Відповіді:


11

Я думаю, що у вас може виникнути непридатність користувача до процесу невідповідності.

По-перше: чи чесно користувач захоче виконати кілька змін у файлі одночасно? Перейменування (яке може включати або не включати зміну шляху?), Зміну власності та, можливо, зміна вмісту файлу (заради аргументу) здаються окремими діями.

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

У цьому випадку, я настійно рекомендую проти будь-якої реалізації , яка посилає кілька подій - RenameFileCommand, MoveFileCommand, ChangeOwnerCommand- для подання цього одного наміри користувача.

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

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

Реалізуючи несподівану систему спокою з переміщенням і перейменуванням файлів, я вважаю за краще забезпечити узгодженість, використовуючи щось на зразок системи eTag - переконайтесь, що версія редагованого ресурсу є версією, яку останній користувач отримав, і не вдасться, якщо вона був змінений з того часу. Але якщо ви відправляєте кілька команд для цієї операції з одним користувачем, вам потрібно буде збільшити версію ресурсу після кожної команди - так що у вас немає ніякого способу знати, що ресурс, який користувач редагує, справді є тією ж версією, що і ресурс, який вони останнім часом читали .

Що я маю на увазі під цим, - що, якщо хтось інший виконує іншу операцію над файлом майже в той же час. Ці 6 команд можуть скластись у будь-якому порядку. Якби у нас було лише 2 атомні команди, попередня команда могла б досягти успіху, а пізніша команда могла провалитися "ресурс був змінений з моменту останнього пошуку". Але від цього немає захисту, коли команди не є атомними, тому послідовність системи порушується.

Цікаво, що в REST є рух на щось подібне до архітектури, заснованої на подіях, під назвою "Відпочинок без PUT", рекомендованої в радіолокаційній програмі Thoughtworks, січень 2015 року . Тут є значно довший блог про відпочинок без путу .

По суті, ідея полягає в тому, що POST, PUT, DELETE та GET чудово підходять для невеликих додатків, але коли вам потрібно почати припускати, як трактування та публікація та видалення можуть бути інтерпретовані на іншому кінці, ви вводите з'єднання. (наприклад, "коли я вилучаю ресурс, пов’язаний з моїм банківським рахунком, рахунок повинен бути закритий") І запропонованим рішенням є лікування REST більш породним способом. тобто дозволяє POST наміру користувача як єдиний ресурс події.

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

Що стосується інших програм, які використовують зовсім інші API для ваших ресурсів. Це пахне неприємностями. Чи можете ви побудувати фасад старого API поверх свого RESTful API та вказати його на фасад? тобто відкрийте службу, яка виконує кілька оновлень файлу послідовно через сервер REST?

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


Я можу побачити невідповідність, однак, REST в принципі можливо оновити стан декількох полів шляхом Введення нового стану в ресурс (за деяким представленням). Ось так працює REST за визначенням - передача репрезентативного стану, що полягає в тому, що наміри користувача втрачаються в процесі. Також REST - це майже стандарт для зовнішнього API. Я просто хочу створити додаток, щоб я міг використовувати обидва. Видалення PUT подібне до вирішення через ES. Як я бачу, одна команда оновлення, що випускає кілька подій, можлива, доки обробка команд і подій знаходиться в одній транзакції.
рудоволосий

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

Я повернусь до цього, після деякого розгляду з більшою думкою про PUT. Звичайно, видалення PUT - це не просто "вирішення для ES". Я виправив посилання на блог.
перфекціоніст

4

Щойно я наткнувся на наступну статтю, яка рекомендує вказувати імена команд у запиті на сервер у заголовку Content-Type (дотримуючись 5 рівнів типу медіа).

У статті вони згадують, що стиль RPC поганий для REST, і пропонують розширити Content-Type, щоб вказати ім'я команди:

Одним із поширених підходів є використання ресурсів у стилі RPC, наприклад / api / InventoryItem / {id} / rename. Хоча це, здавалося б, позбавляє потреби у довільних дієсловах, це суперечить представленню REST, орієнтованому на ресурси. Нам потрібно нагадати, що ресурс - це іменник, а дієслова HTTP - це дієслово / дія, а самоописові повідомлення (один із принципів REST) ​​є засобом передачі інших осей інформації та наміру. Насправді команди в корисному навантаженні повідомлення HTTP повинно бути достатньо для вираження будь-яких довільних дій. Однак, покладаючись на тіло повідомлення, є свої проблеми, оскільки тіло, як правило, доставляється як потік і буферизує тіло в повному обсязі, перш ніж ідентифікувати дії не завжди можливо і не розумно.

PUT /api/InventoryItem/4454c398-2fbb-4215-b986-fb7b54b62ac5 HTTP/1.1
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate,sdch
Content-Type:application/json;domain-model=RenameInventoryItemCommand`

Стаття тут: http://www.infoq.com/articles/rest-api-on-cqrs

Більше про 5 типів медіа можна прочитати тут: http://byterot.blogspot.co.uk/2012/12/5-levels-of-media-type-rest-csds.html


Хоча вони розкривають події домену API API REST, що я вважаю бідною практикою, мені подобається рішення, оскільки він не створює новий "протокол" виключно для CQRS, будь то надсилання імен команд в тіло або додатково заголовка і залишається вірним принципам RESTful.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.