Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ドメインイベントの観点から再考するソフトウェア設計

 ドメインイベントの観点から再考するソフトウェア設計

ドメインイベントは過去に起きたドメイン上の出来事を意味します。「過去に起きた」なので後から変更できません。つまり不変(イミュータブル)なモデルです。

昨今、このドメインイベントはCQRS/Event Sourcingやマイクロサービスなどの書籍で取り上げられ、実際に実装上でドメインイベントが利用される事例も増えています。このように有益性は認識されつつありますが「うちはEvent Sourcingじゃないのでイベントは関係ありません」と視野が狭くなっている方もいます。

たとえ実装で使えなくても、ドメイン分析に基盤的な視点を与えてくれるのがドメインイベントです。

ともあれ、この資料は「そもそもドメインイベントはソフトウェア設計にどのような影響を与えるのか」を解説します。

かとじゅん

November 23, 2021
Tweet

More Decks by かとじゅん

Other Decks in Programming

Transcript

  1. EVENT SOURCINGの利点と欠点 利点 イベントは追記のみで更新がないので、スケーラビリティが 確保しやすい イベントがあればステートを導出できる。過去のどの時点で も 他のマイクロサービスと非同期連携がしやすい 監査ログや行動履歴の分析に利用できる 欠点

    大量のイベントからステートをリプレイする際にレイテンシ が悪化する 原則的にすべてのイベントをストレージに保存する必要があ る 欠点はカバーするソリューションがある 28
  2. CRUDモデル I/Oするのは最新のステートのみ 集約はイミュータブルにできる class AddCartItemUseCase { void execute(CartId cartId, ItemId

    itemId, ItemNum num) { // ランタイムに集約を呼び戻す var cart = cartRepository.findById(cartId); // ドメインロジックを呼び出す var cartUpdated = cart.addItem(itemId, num); // 集約を永続化する cartRepository.store(cartUpdated); } } 32
  3. ESモデル(1) I/Oするのはイベントのみ ステートはイベントから導出する 普通にやるとCRUDモデルより問題がでる イベントからのリプレイ イベントが長大だとレイテンシが悪化する データ競合を防ぐ仕組みが必要 class AddCartItemUseCase {

    void execute(CartId cartId, ItemId itemId, ItemNum num) { // すべてのイベントを読み込む var allEvents = cartEventRepository.findAllEventsById(cartId); // すべてのイベントを空の状態にアプライ var cart = Cart.formEvents(allEvents); // ドメインロジックを呼び出す var itemAdd = cart.addItem(itemId, num); // ステートではなく発生したイベントだけを追記 cartEventRepository.store(itemAdd); } } 34
  4. ESモデル(2) スナップショットでイベントをショートカットする ロックのために集約本体と追記イベントを同一Txで保存する リクエスト毎に余分なI/Oオーバーヘッドがかかる… class AddCartItemUseCase { void execute(CartId cartId,

    ItemId itemId, ItemNum num) { // 最新のスナップショットを読み込む var snapshot = cartEventRepository.findLastestSnapshotById(cartId); // 差分イベントを読み込む var events = cartEventRepository.findAllEventsById( cartId, snapshot.sequenceNumber ); var cart = Cart.formEvents(snapshot, events); // リプレイ // ドメインロジックを呼び出す var newCartWithItemAdd = cart.addItem(itemId, num); // スナップショット保存&イベント追記 cartEventRepository.storeSnapshotWithEvent( newCartWithItemAdd.cart, newCartWithItemAdd.event ); } } 35
  5. シャーディングされた集約アクタ Node1 Node2 Shard1 Shard2 Node3 Shard3 Database API ユースケース

    シャードリージョンプロキシ シャードリージョン1 集約アクター1 集約アクター2 集約アクター3 シャードリージョン2 シャードコーディネータ 集約アクター4 状態は集約内部のドメインオブジェクトが保持する ジャーナル 集約のバックアップログを保存する 集約IDからアクター参照を解決する アクターはシャーディングされるので、同一IDの集約アクターは 同一ノードでしか起動しない。集約へのコマンドメッセージはどの ノードが送信しても特定のノードに配送される。基本的にロック不 要。コマンドは到着順に処理される。 38