イベントソーシングとREST


17

Event Sourcingの設計に出会い、RESTクライアントが必要なアプリケーションで使用したいと思います(正確にはRESTfulです)。ただし、RESTは非常にCRUDに似ており、イベントソーシングはタスクベースであるため、これらを接続することはできません。RESTサーバーへの要求に基づいてコマンドの作成をどのように設計できるのか疑問に思いました。この例を考えてみましょう:

RESTを使用すると、Fileというリソースに新しい状態を設定できます。1つのリクエストで、新しいファイル名を送信したり、親フォルダーを変更したり、ファイルの所有者を変更したりできます。

イベントソーシングを使用できるようにサーバーを構築する方法。私はこれらの可能性について考えていました:

  1. フィールドが変更されたサーバ上で決定し、適切なコマンドを作成する(RenameFileCommandMoveFileCommandChangeOwnerCommand、...)と個別にこれらを派遣。ただし、このセットアップでは、各コマンドが失敗し、他のリソースがトランザクションから除外され、リソースへの「アトミックな」変更から除外されます。

  2. 発送のみ一つのコマンド(UpdateFileCommand)およびコマンドハンドラでは、より正確に集計では、変更されたフィールドを決定し、代わりに個々のイベントを送信します(FileRenamedEventFileMovedEventOwnerChangedEvent、...)

  3. これはまったく好きではありません:サーバーへのリクエストでは、ヘッダーで使用するコマンドを指定します。UIはまだタスクベースです(ただし、通信はRESTを介して行われます)。ただし、REST通信のその他の使用(外部アプリなど)では失敗します。1つのリクエストで1つのフィールドのみを変更するようにバインドされているためです。また、UI、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

ここで、ユーザープロセスと実装の不一致があるかもしれません。

まず、ユーザーは正直にファイルに複数の変更を同時に実行したいのでしょうか?名前の変更(パスの変更を含む場合と含まない場合があります)、所有権の変更、およびファイルの内容の変更(引数のため)は、個別のアクションのように見えます。

答えが「はい」である場合を考えてみましょう-ユーザーは本当にこれらの変更を同時に行いたいと思っています。

その場合、この単一のユーザーの意図を表すためRenameFileCommandMoveFileCommand、複数のイベント(、)を送信する実装に対して強くお勧めします。ChangeOwnerCommand

どうして?イベントが失敗する可能性があるため。非常にまれかもしれませんが、ユーザーがアトミックな操作を送信しました。ダウンストリームイベントの1つが失敗すると、アプリケーションの状態は無効になります。

また、各イベントハンドラー間で明確に共有されているリソースに人種の危険を招きます。コマンドを受信するまでにファイル名とファイルパスが古くなっている可能性があるため、「ChangeOwnerCommand」を記述する必要があります。

ファイルを移動して名前を変更する非イベント駆動の安らかなシステムを実装する場合、eTagシステムのようなものを使用して一貫性を確保することを好みます-編集中のリソースのバージョンがユーザーが最後に取得したバージョンであることを確認し、それが失敗した場合それ以来変更されています。ただし、この単一のユーザー操作のために複数のコマンドをディスパッチしている場合は、各コマンドの後にリソースバージョンをインクリメントする必要があります。したがって、ユーザーが編集しているリソースが実際に最後に読んだリソースと同じバージョンであることを知る方法がありません。

つまり、他の誰かがほぼ同時にファイルに対して別の操作を実行するとしたらどうでしょう。6つのコマンドは任意の順序でスタックできます。アトミックコマンドが2つしかない場合、前のコマンドは成功し、後のコマンドは「リソースが最後に取得されてから変更されました」と失敗する可能性があります。しかし、コマンドがアトミックでない場合、これに対する保護はないため、システムの一貫性が侵害されます。

興味深いことに、2015年1月のThoughtworksテクノロジーレーダーで推奨されている「PUTなしのレスト」と呼ばれる、RESTのイベントベースのアーキテクチャのようなものへの動きがあります。ここには、PUTなしの休憩に関するかなり長いブログがあります

基本的には、POST、PUT、DELETE、およびGETは小規模なアプリケーションには適していますが、反対側でputおよびpostおよびdeleteがどのように解釈されるかを想定する必要がある場合は、カップリングを導入します。(たとえば、「銀行口座に関連付けられたリソースを削除する場合、口座を閉鎖する必要があります」)提案された解決策は、RESTをよりイベントソースの方法で処理することです。つまり、ユーザーの意図を単一のイベントリソースとしてPOSTできます。

他のケースはより簡単です。ユーザーがこれらすべての操作を同時に実行したくない場合は、許可しないでください。各ユーザーの意図に対してイベントをPOSTします。これで、リソースでetagバージョン管理を使用できます。

リソースに対して非常に異なるAPIを使用している他のアプリケーションについては。それはトラブルのようなにおいがする。RESTful APIの上に古いAPIのファサードを構築し、それらをファサードに向けることができますか?つまり、RESTサーバーを介してファイルに複数の更新を順番に実行するサービスを公開しますか?

古いソリューションの上にRESTfulインターフェイスを構築せず、RESTソリューションの上に古いインターフェイスのファサードを構築せず、共有データリソースを指す両方のAPIを維持しようとすると、大きな頭痛の種になります。


不一致は確認できますが、RESTでは原則として、新しい状態をリソースに(何らかの表現で)PUTすることで複数のフィールドの状態を更新することができます。これは、定義によりRESTがどのように機能するかです-表現状態の転送、欠点は、ユーザーの意図がプロセスで失われることです。また、RESTは外部APIのほぼ標準です。両方を使用できるようにアプリを設計するだけです。ESのため、PUTを削除することは回避策のようなものです。私が見ることができることから、複数のイベントを発行する1つの更新コマンドは、コマンドとイベント処理が1つのトランザクション内にある限り実行可能です。
赤毛

Etagはとにかくやりたいことです。また、「より長いブログ投稿」へのリンクは、最初のリンクと同じです。
赤毛

PUTについてさらに考えて検討した後、これに戻ります。確かに、PUTの削除は「ESの回避策」ではありません。ブログのリンクを修正しました。
完璧主義者

4

たった今、次の記事に出くわしました。これは、Content-Typeヘッダーでサーバーへのリクエストにコマンド名を指定することを推奨しています(5つのメディアタイプのレベルを追跡しています)。

記事では、RPCスタイルはRESTに悪いと言及しており、Content-Typeを拡張してコマンド名を指定することを提案しています。

一般的なアプローチの1つは、/ api / InventoryItem / {id} / renameなどのRPCスタイルのリソースを使用することです。これにより、任意の動詞が不要になりますが、RESTのリソース指向のプレゼンテーションに反します。リソースは名詞であり、HTTP動詞は動詞/アクションであり、自己記述メッセージ(RESTの教義の1つ)は他の軸の情報と意図を伝える手段であることに注意する必要があります。実際、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をご覧ください。


彼らはドメインイベントをREST APIに公開していますが、これは悪い習慣だと思いますが、CQRS専用の新しい「プロトコル」を作成しないので、本文または追加でコマンド名を送信するソリューションが好きですヘッダー、およびRESTful原則に忠実です。

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