LoginSignup
15
10

More than 5 years have passed since last update.

EventStoreをMicroservicesとして実装するときのプロトコルの案

Last updated at Posted at 2014-11-18

EventStoreはappend-only databaseの一種で、ドメインイベントを蓄積するためのストレージである。イベントは追加(append)だけでき、一旦追加したイベントは変更・削除することができない。イベントストアに蓄積された情報をもとに、読み取り専用のためのドメインモデルであるリードモデル(read model)やクエリーモデル(query model)を作ることを、イベントソーシング(event sourcing)と言う。

このドキュメントはEventStoreをMicroservicesとして提供することを念頭に、そのサービスが実装するであろうRESTful APIのプロトコルを検討するものだ。まだ未確定の部分がある。ご指摘などがあれば歓迎。

各URLの/stores/:store_nameはマルチテナントのEventStoreを実装する場合に必要になるであろう名前空間だ。マルチテナントを実装しない場合は、この階層は不要になる。

イベントを追加する

PUT /stores/:store_name/streams/:stream_name

Input

JSON形式

Name Type Description
event_type string Required. イベントの型。
stream_version int Required. イベントストリームのバージョン。楽観ロックのチェックに使う。
body string Required. イベントの中身。オブジェクトであればシリアライズされたもの。
sha1 string Required. bodyのチェックサム。

Response

HTTP/1.1 204 No Content

Errors

Status Code Description
400 Bad Request missing_field 必須の入力がないとき。
400 Bad Request stream_version.must_be_numeric stream_versionが正の整数でないとき。
400 Bad Request checksum.did_not_match checksumの値が一致しなかったとき。
409 Conflict stream_version.conflict 与えられたstream_versionが同一のイベントが既に追加されているとき。または、与えられたstream_versionが連番でないとき。つまり、イベントストリームの最新のイベントのstream_version+1がリクエストのstream_versionと同一でないとき。

イベントストリームを取得する

GET /stores/:store_name/streams/:stream_name

Input

クエリーストリング

Name Type Description
since int イベントIDを指定すると、そのバージョン以降のイベントのみがレスポンスになる。

Response

ストリーム全体をJSONとして返すと、クライアントは全て受け取ってから処理しなければならない。Server-sent events(SSE)や Multipartでレスポンスするように実装すると、逐次処理ができるのでクライアントに優しい。

SSEとMultipartの両方を実装するのであれば、Acceptヘッダーを見てレスポンスの内容を変える方法がある。

SSEでほしいときのリクエストヘッダ
Accept: text/event-stream

Headers

Name Type Description
Stream-Name string イベントストリーム名。
Stream-Version int イベントストリームバージョン。

Body

Name(SSE) Name(Multipart) Type Description
id 各パートのEvent-Idヘッダ int イベントID。
event 各パートのEvent-Typeヘッダ string イベントの型。
data 各パートのBody部分 string イベントの中身。

Server-sent eventsの例

HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: text/event-stream
Transfer-Encoding: chunked
Stream-Name: 9dced0a4-2727-4e9a-ae65-378135c06565
Stream-Version: 2

id: 60
event: PostCreated
data: {
data:   "post_id": "9dced0a4-2727-4e9a-ae65-378135c06565",
data:   "title": "テスト",
data:   "created_by": 72,
data:   "occurred_on": "2014-11-18T12:07:15Z"
data: }

id: 87
event: TitleChanged
data: {
data:   "post_id": "9dced0a4-2727-4e9a-ae65-378135c06565",
data:   "title": "タイトル変更",
data:   "occurred_on": "2014-11-18T15:22:44Z"
data: }

Multipartの例

HTTP/1.1 200 OK
Content-Length: 26814
Content-Type: multipart/mixed; boundary=t9bhpX6qJvUDzk7X6Acsf4TX
Stream-Name: 9dced0a4-2727-4e9a-ae65-378135c06565
Stream-Version: 2

--t9bhpX6qJvUDzk7X6Acsf4TX

Event-ID: 60
Event-Type: PostCreated
Content-Type: application/json; charset=UTF-8

{
  "post_id": "9dced0a4-2727-4e9a-ae65-378135c06565",
  "title": "テスト",
  "created_by": 72,
  "occurred_on": "2014-11-18T12:07:15Z"
}

--t9bhpX6qJvUDzk7X6Acsf4TX

Event-ID: 60
Event-Type: TitleChanged
Content-Type: application/json; charset=UTF-8

{
  "post_id": "9dced0a4-2727-4e9a-ae65-378135c06565",
  "title": "タイトル変更",
  "occurred_on": "2014-11-18T15:22:44Z"
}

--t9bhpX6qJvUDzk7X6Acsf4TX--

Errors

Status Code Description
404 Not Found missing イベントストリームが見つからない。つまり、イベントがひとつもないストリームについてリクエストしたとき。

イベント全体を取得する

GET /stores/:store_name/events

Input

クエリーストリング

Name Type Description
since int イベントIDを指定すると、そのバージョン以降のイベントのみがレスポンスになる。

Response

まだイベントがひとつもないときでも、404にはならない。

Body

Name(SSE) Name(Multipart) Type Description
id 各パートのEvent-Idヘッダ int イベントID。
event 各パートのEvent-Typeヘッダ string イベントの型。
data 各パートのBody部分 string イベントの中身。

Server-sent eventsの例

HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: text/event-stream
Transfer-Encoding: chunked

id: 60
event: PostCreated
data: {
data:   "post_id": "9dced0a4-2727-4e9a-ae65-378135c06565",
data:   "title": "テスト",
data:   "created_by": 72,
data:   "occurred_on": "2014-11-18T12:07:15Z"
data: }

id: 61
event: PostCreated
data: {
data:   "post_id": "c85848e8-3223-444f-9305-b07ea10000f7",
data:   "title": "テスト",
data:   "created_by": 43,
data:   "occurred_on": "2014-11-18T12:11:23Z"
data: }

id: 62
event: PostDeleted
data: {
data:   "post_id": "c679ca51-3a70-4a37-8a62-42b88e5f9c02",
data:   "occurred_on": "2014-11-18T13:40:16Z"
data: }

Multipartの例

HTTP/1.1 200 OK
Content-Length: 26814
Content-Type: multipart/mixed; boundary=t9bhpX6qJvUDzk7X6Acsf4TX

--t9bhpX6qJvUDzk7X6Acsf4TX

Event-ID: 60
Event-Type: PostCreated
Content-Type: application/json; charset=UTF-8

{
  "post_id": "9dced0a4-2727-4e9a-ae65-378135c06565",
  "title": "テスト",
  "created_by": 72,
  "occurred_on": "2014-11-18T12:07:15Z"
}

--t9bhpX6qJvUDzk7X6Acsf4TX

Event-ID: 61
Event-Type: PostCreated
Content-Type: application/json; charset=UTF-8

{
  "post_id": "c85848e8-3223-444f-9305-b07ea10000f7",
  "title": "テスト",
  "created_by": 43,
  "occurred_on": "2014-11-18T12:11:23Z"
}

--t9bhpX6qJvUDzk7X6Acsf4TX

Event-ID: 62
Event-Type: PostDeleted
Content-Type: application/json; charset=UTF-8
{
  "post_id": "c679ca51-3a70-4a37-8a62-42b88e5f9c02",
  "occurred_on": "2014-11-18T13:40:16Z"
}

--pX6qJvUDzk7X6Acsf4TX--

イベントストアを空っぽにする (管理用)

POST /stores/:store_name/purge

Response

HTTP/1.1 204 No Content

ストリームが存在するか?

HEAD /stores/:store_name/streams/:stream_name

Response

todo

ストリーム一覧 (管理用)

GET /stores/:store_name/streams

Response

todo

イベントストアを作る (管理用)

POST /stores

Input

JSON形式

Name Type Description
store_name string Required. イベントストア名。

Response

todo

イベントストア一覧 (管理用)

GET /stores

Response

todo

??? (管理用)

GET /stores/:store_name

Response

todo

イベントストア名を変更する (管理用)

todo

イベントストアを削除する (管理用)

DELETE /stores/:store_name
15
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
10