🐥
CQRS/ES 俺的ベストプラクティス②
イベントはイミュータブルに保つ
イベントソーシングを利用したシステムでは、「一度イベントストアに保存されたイベントは不変(イミュータブル)であるべき」という原則があります。
これはイベントとは過去の歴史であり、改変してはならないものだからです。
しかし、長期にわたるシステム運用の中で必ずイベントのスキーマ(構造)を変更したくなる場面がやってきます。
- フィールドの追加・削除
- 型の変更や形式の変化
- etc....
では、イベントの構造が途中で変わる場合、CQRS/ESではどのように対処するべきなのでしょうか?
答えは以下の2択です。
- イミュータブルなイベント設計を取り入れる
- イベントの変換処理を書く
個人的にはイミュータブルなイベント設計を取り入れることを強くお勧めします
なぜイベントの変換処理はダメなのか?
たとえば、以下のような依存関係のマイクロサービス群があるとします。
この場合、先に C , B, Dのサービスに対してイベントの変換処理を実装してから、 Aでイベントを発行しなくてはならず、管理が恐ろしく大変になります。
また、例えばDというサービスがイベントの変換処理の実装を忘れていたとします。
この時、 Aサービスから発行された最新のイベントのスキーマとDサービスのスキーマが異なるので、
Dサービスではイベントキャッチできなくなりエラーが起こります。
イミュータブルイベント最高
イミュータブルイベントの場合、Aサービスは新しいイベント (入庫したv2)のようなイベントを定義しpublishするだけで済みます。
C, B, Dサービスは購読の準備ができていない場合、キャッチできないだけでエラーにはなりません。
kotlinの例
今回はAxonFrameworkにおいて、イベントのどのように定義するかの例を示します
なお、このやり方は完全に我流ですが多分問題ないと思います。
// イベントを表すクラスを大枠に作るよ。
class ProductCreatedEvent {
// イベントはこんな感じで内部クラスとして表現するよ。
data class V1(
val id: String,
val brandId: String,
val name: String,
val description: String,
val imageUrl: String?,
val price: Int,
val quantity: Int,
)
data class V2(
val id: String,
val brandId: String,
// V2になってジャンルIDが追加されたよ。
val genreId: String,
val name: String,
val description: String,
val imageUrl: String?,
val price: Int,
val quantity: Int,
)
}
Discussion