REST APIはコマンド/アクションベースのドメインにどのように適合しますか?


24

この記事の著者は、

時には、本質的にRESTfulではない操作をAPIで公開する必要があります。

そしてそれ

APIのアクションが多すぎる場合は、RESTful原則を使用するのではなくRPCの観点で設計されているか、問題のAPIがRPCタイプモデルにより適していることを示しています。

これは、他の場所でも読んだり聞いたりしたことを反映しています。

しかし、これは非常に紛らわしいと思うので、問題をよりよく理解したいと思います。

例I:RESTインターフェースを介してVMをシャットダウンする

VMのシャットダウンをモデル化するには、根本的に異なる2つの方法があると思います。それぞれの方法にはいくつかのバリエーションがありますが、ここでは最も基本的な違いに集中しましょう。

1.リソースの状態プロパティにパッチを適用します

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

{ "state": "shutting down" }

(または、PUTサブリソース上/api/virtualmachines/42/state。)

VMはバックグラウンドでシャットダウンし、シャットダウンが成功するかどうかに応じて、後のある時点で状態が「電源オフ」で内部的に更新される可能性があります。

2.リソースのアクションプロパティのPUTまたはPOST

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

{ "type": "shutdown" }

結果は、最初の例とまったく同じです。状態はすぐに「シャットダウン」に更新され、最終的には「電源オフ」に更新されます。

どちらのデザインもRESTfulですか?

どちらのデザインが優れていますか?

例II:CQRS

複数の集約の更新につながる可能性のある、または具体的なリソースとサブリソースのCRUD操作にマッピングできない「アクション」(別名コマンド)が多数あるCQRSドメインがある場合はどうなりますか?

具体例で可能な限り多くのコマンドを具体的なリソースで作成または更新し(例Iの最初のアプローチに従って)、残りの部分に「アクションエンドポイント」を使用してみてください。

または、すべてのコマンドをアクションエンドポイントにマッピングする必要があります(例Iの2番目のアプローチのように)。

どこで線を引きますか?設計のRESTful性が低下するのはいつですか?

CQRSモデルは、APIのようなRPCにより適していますか?

上記の引用テキストによると、私が理解しているとおりです。

私の多くの質問からわかるように、このトピックについて少し混乱しています。それをよりよく理解するのを手伝ってもらえますか?


「アクションの作成」は、実行されたアクションがその後独自のリソース識別子を持つ場合を除いて、RESTfulとは思えません。それ以外の場合、PATCHまたはPUTを使用して「状態」プロパティを変更する方が理にかなっています。CQRSの部分については、まだ良い答えがありません。
ファビアンシュメングラー16

3
@Laivそれに問題はありません。これは学術的な質問です。この問題についてより深く理解したいと思います。
leifbattermann 16

回答:


19

最初のケース(VMのシャットダウン)では、RESTfulのOPの代替案はどれも考えていません。確かに、Richardson成熟度モデルを尺度として使用する場合、リソースと動詞を使用するため、どちらもleve 2 APIです。

ただし、どちらもハイパーメディアコントロールを使用していません。私の意見では、RESTful APIデザインとRPC を区別する唯一のタイプのRESTです。言い換えれば、レベル1と2に固執すると、ほとんどの場合RPCスタイルのAPIを使用することになります。

VMをシャットダウンする2つの異なる方法をモデル化するために、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 Cookbookに概説されているように、リレーションシップタイプにはIRIまたはより精巧な識別スキームを使用しますが、例をできるだけシンプルにすることを選択しました。)

この場合、アクションで提供する情報はほとんどないため、クライアントは次のURLに対して空のPOSTを作成するだけですhref

POST /vms/1234/fdaIX HTTP/1.1

(このリクエストにはボディがないため、これをGETリクエストとしてモデル化するのは魅力的ですが、GETリクエストには目に見える副作用はないはずなので、POSTの方が正確です。)

同様に、クライアントがVMの電源をオフにしたい場合、power-off代わりにリンクをたどります。

つまり、リンクの関係タイプは、意図を示すアフォーダンスを提供します。各関係タイプには、特定の意味上の意味があります。これが、セマンティックWebについて時々話す理由です。

この例をできるだけ明確にするために、各リンクのURLを意図的に隠しています。ホスティングサーバーが着信要求を受信すると、それfdaIXシャットダウンを意味電源オフCHTY91意味ます。

通常、私はURLでアクションをエンコードするだけなので、URLは/vms/1234/shut-downandになります/vms/1234/power-offが、教えると、関係タイプ(セマンティクス)とURL(実装の詳細)の区別があいまいになります。

使用しているクライアントに応じて、RESTful URLをハッキング不可能にすることを検討できます

CQRS

CQRSに関しては、Greg YoungとUdi Dahanが同意している数少ない点の1つは、CQRSはトップレベルのアーキテクチャではないということです。したがって、RESTful APIをCQRSに似たものにすることには注意が必要です。これは、クライアントがアーキテクチャの一部になることを意味するためです。

多くの場合、実際の(レベル3)RESTful APIの背後にある原動力は、クライアントを壊すことなく、またクライアントを制御することなく、APIを進化させたいということです。それがあなたの動機であるなら、CQRSは私の最初の選択ではないでしょう。


最初の例はどちらもハイパーメディアコントロールを使用しないため、どちらもRESTfulではありませんか?ただし、応答も投稿せず、リクエストURLと本文のみを投稿しました。
leifbattermann

4
@leifbattermannメッセージ本文を使用して意図を伝えるため、RESTfulではありません。それは明らかにRPCです。これらのリソースに到達するためにリンクを使用した場合、なぜ身体を通して意図を伝える必要があるのでしょうか?
マークゼーマン

それは理にかなっている。なぜPOSTを提案するのですか?アクションはべき等ではありませんか?いずれにせよ、使用するHTTPメソッドをクライアントにどのように伝えるのですか?
leifbattermann 16

2
@ guillaume31 DELETEは私にとっては奇妙に思えます。なぜなら、シャットダウンした後もvmは「power off」状態(またはそのような状態)でしか存在しないからです。
leifbattermann 16

1
リソースは、VMを一時的に反映する必要はなく、その実行インスタンスを表すことができます。
guillaume31

6

RESTインターフェースを介したVMのシャットダウン

これは実際には2009年にティム・ブレイが発表したやや有名な例です。

問題について議論しているロイ・フィールディングは、この見解を共有しました

私は個人的に、監視状態(電源ステータスなど)を編集不可として扱うシステムを好みます。

つまり、監視状態の現在の表現を返す情報リソースが1つあります。その表現には、その状態への変更を要求するフォームへのハイパーメディアリンクが含まれる場合があり、フォームには(各)変更要求を処理するリソースへの別のリンクがあります。

セス・ラッドはこの問題に関する重要な洞察を持っていました

ランニングを、人の単純な状態から、作成、更新、話し合うことができる真の名詞に変えました。

これをマシンの再起動に戻します。/ vdc / 434 / cluster / 4894 / server / 4343 / rebootsにPOSTすることをお勧めします。投稿したら、この再起動を表すURIがあり、ステータスの更新のためにそれを取得できます。ハイパーリンクの魔法により、再起動の表現は再起動されるサーバーにリンクされます。

URIスペースの作成は安価で、URIはさらに安価だと思います。名詞としてモデル化されたアクティビティのコレクションを作成し、POST、PUT、およびDELETEを実行します!

RESTfulプログラミングは、Web規模でのVogonの官僚主義です。あなたはどのように行うのですかのRESTfulは?新しい書類を作成し、書類をデジタル化します。

やや手の込んだ言語では、「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を1か所にまとめて複数のリソースを同時に変更するのではなく、各PUTに固有の識別子を与えることをお勧めします。

URIのスペルについて説明している限り、RESTは気にしません。/cc719e3a-c772-48ee-b0e6-09b4e7abbf8bは、RESTに関する限り、完全なcromulent URIです。変数名と同様に、読みやすさは別の懸念事項です。RFC 3986に準拠した綴りを使用すると、人々はずっと幸せになります。

CQRS

複数の集約の更新につながる可能性のある、または具体的なリソースとサブリソースのCRUD操作にマッピングできない「アクション」(別名コマンド)が多数あるCQRSドメインがある場合はどうなりますか?

CQRSのGreg Young

CQRSは、他の方法では存在しない可能性のあるアーキテクチャの多くの機会を可能にする非常に単純なパターンです。CQRSは結果整合性ではなく、イベント発生ではなく、メッセージングではなく、読み取りと書き込みのモデルが分離されておらず、イベントソーシングも使用されていません。

ほとんどの人がCQRSについて話すとき、彼らは実際にアプリケーションのサービス境界を表すオブジェクトにCQRSパターンを適用することについて話します。

HTTP / RESTのコンテキストでCQRSについて話していることを考えると、この後者のコンテキストで作業していると仮定するのは合理的なように思えます。

驚くべきことに、これは前の例よりもさらに簡単です。その理由は簡単です。コマンドはメッセージです。

Jim Webberは、1950年代のオフィスのアプリケーションプロトコルとしてHTTPを説明しています。メッセージを受け取って受信トレイに入れることで作業が完了します。同じ考えが成り立ちます-フォームの空のコピーを取得し、私たちが知っている詳細を記入して配信します。タダ

具体例で可能な限り多くのコマンドを具体的なリソースで作成または更新し(例Iの最初のアプローチに従って)、残りの部分に「アクションエンドポイント」を使用してみてください。

はい、「具体的なリソース」がドメインモデルのエンティティではなくメッセージである限り。

重要なアイデア:REST APIはまだインターフェースです。クライアントがメッセージを変更しなくても、基礎となるモデルを変更できるはずです。新しいモデルをリリースすると、ドメインプロトコルを取得して新しいモデルに適用する方法を知っているWebエンドポイントの新しいバージョンをリリースします。

CQRSモデルは、APIのようなRPCにより適していますか?

実際はそうではありません-特に、Webキャッシュは「最終的に一貫した読み取りモデル」の優れた例です。各ビューを個別にアドレス指定可能にし、それぞれに独自のキャッシングルールを設定すると、多くのスケーリングが無料で提供されます。読み取りに対する排他的RPCアプローチには比較的魅力がありません。

書き込みに関しては、より厄介な質問です。すべてのコマンドを単一のエンドポイントまたは単一のエンドポイントファミリーの単一ハンドラーに送信することは確かに簡単です。RESTは、エンドポイントがクライアントに対してどこにあるかを見つける方法を本当に重視しています。

メッセージを独自のリソースとして扱うことには、PUTを使用できるという利点があり、メッセージの処理がべき等であるという事実を中間コンポーネントに警告するため、エラー処理の特定のケースに参加することができます。(注:クライアントの観点から、リソースが異なるURIを持っている場合、それらは異なるリソースであることに注意してください。すべてがオリジンサーバー上で同じリクエストハンドラコードを持っている可能性があるという事実は、ユニフォームによって隠された実装の詳細ですインタフェース)。

フィールディング(2008)

また、上記はまだ完全にRESTfulではなく、少なくともこの用語の使用方法に注意してください。私がやったことは、RPCにすぎないサービスインターフェイスの説明だけです。RESTfulにするには、ハイパーテキストを追加してサービスを導入および定義し、フォームやリンクテンプレートを使用してマッピングを実行する方法を説明し、視覚化を便利な方法で組み合わせるコードを提供する必要があります。


2

5レベルのメディアタイプを利用して、リクエストのcontent-typeヘッダーフィールドでコマンドを指定できます。

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の前の記事とそれから多くを学びました。
ピーターL

1

リンクされた記事の例は、マシンの起動とシャットダウンは、モデル化されたリソースの状態の変化ではなく、コマンドによって指示される必要があるという考えに基づいています。後者は、RESTが生きて呼吸するものです。VMのモデリングを改善するには、実際のVMがどのように機能するか、そして人間としてのVMとの対話方法を調べる必要があります。これは時間のかかる作業ですが、優れたモデリングを行うために必要な思考の種類についての良い洞察を与えると思います。

デスク上のコンピューターの電源状態を制御するには、2つの方法があります。

  • 電源スイッチ: 電源への電気の流れを即座に遮断し、コンピューター全体を突然無秩序に停止させます。
  • オン/オフボタン: 外部の何かがすべてのシャットダウンを望んでいることをソフトウェアに通知するようにハードウェアに指示します。ソフトウェアは正常なシャットダウンを行い、完了したことをハードウェアに通知し、ハードウェアはスタンバイ状態に移行できることを電源に通知します。電源スイッチがオンになっていて、マシンが稼働しており、ソフトウェアがシャットダウン信号に応答できない状態にある場合、電源スイッチをオフにしない限りシステムはシャットダウンしません。(VMはまったく同じように動作します。シャットダウン信号がソフトウェアによって無視された場合、マシンは実行を継続します。強制的に電源をオフにする必要があります。)マシンを再起動できるようにするには、電源スイッチをオンに戻し、オン/オフボタンを押します。(多くのコンピューターには、電源ボタンを長押しして直接スタンバイ状態にするオプションがありますが、しかし、このモデルは実際にはそれを必要としません。)このボタンは、押すと状態に応じて異なる動作になるため、トグルスイッチのように扱うことができます。電源スイッチがオフの場合、このボタンを押しても何も起こりません。

VMの場合、これらは両方とも読み取り/書き込みブール値としてモデル化できます。

  • power-に変更するtrueと、スイッチがこの状態になったことを示すメモが作成される以外は何も起こりません。に変更するfalseと、VMは即時に電源オフ状態になります。完全を期すために、値が書き込み後に変更されない場合、何も起こりません。

  • onoff-に変更した場合、のtrue場合powerは何も起こりませんfalse。に変更したfalse場合、powerisの場合は何も起こりませんfalse。それ以外の場合、VMはソフトウェアに正常なシャットダウンを行うように指示されます。繰り返しますが、完全を期すために、変更なしの書き込みは何も行いません。

このすべてにより、マシンの状態がスイッチの状態を反映しない状況が1つあり、それがシャットダウン中にあるという認識があります。 powerまだだろうtrueonoffされますfalseが、プロセッサは、まだそのシャットダウンを実行しており、そのために私たちは、マシンが実際に行っていることを伝えることができるように別のリソースを追加する必要があります。

  • runningtrue-VMが実行されているfalseときと実行されていないときの読み取り専用の値。ハイパーバイザーにその状態を問い合わせることによって決定されます。

その結果、VMを起動するにはpoweronoffリソースとリソースが設定されていることを確認する必要がありtrueます。(あなたは可能性がありますpowerステップは、自己リセットを行うことによりスキップするので、に設定されている場合ということfalse、それはなるとtrueVMが検証可能ハード停止された後。実行するRESTfulに、純粋なことだかどうかは別の議論のための飼料です。)あなたはそれが通常のシャットダウンを行いたい場合は、設定onoffするfalseと、マシンが実際に停止したかどうかを確認するために後で戻ってくる、設定powerするfalseことはしなかった場合。

現実の世界と同様に、VMがonoff変更されfalseた後でもVMを起動するように指示されるという問題がありますが、それrunningはシャットダウン中のためです。それにどう対処するかは政策決定です。


0

どちらのデザインもRESTfulですか?

したがって、安らかに考えたい場合は、コマンドを忘れてください。クライアントは、VMをシャットダウンするようサーバーに指示しません。クライアントは、状態を更新してリソース表現のコピーを「比do的に言えば」(比speaking的に言えば)「サーバー」に戻します。サーバーはその新しい状態表現を受け入れ、この副作用として、実際にVMをシャットダウンします。副作用の側面はサーバーに任されています。

少ないです

サーバー、ここのクライアント、VMをシャットダウンしてもいいですか

などの

ここでサーバー、クライアント、リソースVM 42の状態をシャットダウン状態に更新し、このリソースのコピーを更新してから、適切と思われることを実行します

サーバーがこの新しい状態を受け入れる副作用として、実際に実行する必要のあるアクション(VM 42を物理的にシャットダウンするなど)を確認できますが、これはクライアントに対して透過的です。クライアントは、この新しい状態と一貫性を保つためにサーバーが取らなければならないアクションに関心がありません。

コマンドを忘れてください。唯一のコマンドは、状態転送用のHTTPの動詞です。クライアントは、サーバーが物理VMをシャットダウン状態にする方法を知りませんし、気にしません。クライアントはこれを実現するためにサーバーにコマンドを発行するのではなく、これが新しい状態であると言っているだけです。

これの利点は、フロー制御の観点からクライアントをサーバーから切り離すことです。後でサーバーがVMでの動作を変更しても、クライアントは気にしません。更新するコマンドエンドポイントはありません。RPCでAPIエンドポイントをからshutdownに変更した場合shut-down、サーバーで呼び出すコマンドを知らないため、すべてのクライアントが壊れています。

RESTは宣言型プログラミングに似ています。宣言型プログラミングでは、何かを変更するための一連の命令をリストするのではなく、自分がどのようになりたいかを述べ、プログラミング環境に認識させます。


あなたの答えのためのTHX。クライアントとサーバーの分離に関する2番目の部分は、私自身の理解と非常によく一致しています。回答の最初の部分をバックアップするリソース/リンクはありますか?リソース、HTTPメソッド、ハイパーメディア、自己記述的なメッセージなどを使用すると、どのREST制約が壊れますか?
レイフバッターマン

リソース、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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.