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

CQRSはEvent Sourcingなしで実現できるのか?

CQRSはEvent Sourcingなしで実現できるのか?

933291444e456bfb511a66a2fa9c6929?s=128

かとじゅん

March 24, 2020
Tweet

Transcript

  1. CQRSはEvent Sourcingなしで実現できるのか? かとじゅん(@j5ik2o) BPStudy#151〜オブジェクト指向、モデリング、設計 LT大会[リモート開催] 2020/03/30

  2. © Chatwork あなたは誰? • 加藤潤一(@j5ik2o) • Chatwork社のテックリード • 直近の活動 ◦

    Kafka, S3のEvent Sourcingする ためのプラグインを開発 ◦ Fargateを使った負荷試験ツール の開発
  3. © Chatwork FYI: ChatworkのEvent Sourcing事例 • 2016年にNTTデータさんと共同開発したプロジェクトでCQRS+ESを 採用した(詳細は以下スライド参照) • Apache

    Kafkaの以下の書籍に事例が紹介されています
  4. © Chatwork CQRSはEvent Sourcingなしで実現できるのか? • 今日は「モノとの繋がりをトレースできるイベント= コトはドメイン分析に使える」という話題ではなく、 イベントを使ったアーキテクチャに関する話題 • CQRSにはEvent

    Sourcingは必須?大袈裟では? • State Sourcing(CRUD)のままで、CQRSをすること ができるかどうか考えてみたい
  5. © Chatwork CQRS • Command and Query Responsibility Segregation(CQRS責務分離) ◦

    2010年 Greg Young氏 • CQS(Command-Query Separation) をアーキテクチャレベルに適用 • CommandとQueryで要件が違うから わける ◦ 一貫性(強い or 弱い) ◦ データ形式(正規化 or 非正規化) ◦ スケーラビリティ(C:Q=2:8)
  6. © Chatwork FYI: C/Qを分離しない場合の弊害 • リポジトリのクエリメソッドが複雑になる ◦ employeeRepository.findByDeptIdsWithEmpNamesWith…(…, …, …)

    • N+1クエリが発生しやすい reservationRepository.findByIds(ids).map { reservation => val a = hotelRepository.findById(reservation.hotelId) val b = customerRepository.findById(reservation.customerId) readModelDto(a, b) } • オブジェクト→DTO変換が非効率 ◦ DTOに変換するときに、UIに合わせて捨てられる項目がある
  7. © Chatwork FYI: リードモデルの形式変換をしない場合の問題 • 例えば、入社日がEpocTime(Long値)として保存される場合、そのまま ではクエリのレスポンスに含めることができない。しかしクエリ側から 値オブジェクトに依存ができない。 • これを回避するためにRMUで事前に形式変換する必要がある

    Attribute Type Value EmployeeId String “123456789” JoinedDate Long 1585039714572 ... ... .. JoinedDate#asFormattedString 2020年03月24日 08:48 内部データ表現を意味のある値表現にするにはドメ インオブジェクトを使う必要がある エンコードされたドメインオブジェクト リードしたい値表現
  8. © Chatwork C/Qのモデル間の変換・同期の問題はイベントで解決 “The model that is best suited is

    the introduction of events, events are a well known integration pattern and offer the best mechanism for model synchronization.” by Greg Young 最も適したモデルはイベントの導入であり、イベントは よく知られた統合パターンであり、モデルの同期化に最 適なメカニズムを提供します。
  9. © Chatwork Event Sourcing • 過去に起きた出来事=イベント • イベントは不変で追記のみ • イベントを再生(リプレイ)すると最新状態が手に入る

  10. © Chatwork CQRS+Event Sourcing • 真のデータソースは永続されたイベ ント • そのイベントを使ってリードモデル を構築する

    Event Sourcingは大袈裟で大変と いう意見を聞くので、他の方法がな いか考えてみた
  11. © Chatwork 真のデータソースを最新状態にできないか

  12. © Chatwork アプリケーションからのイベント伝搬方式 • Write DBへ最新状態をロック付きで書き込み、成功したらキューにイベ ントを書き込む方法。これは非常にまずい方法。

  13. © Chatwork アプリケーションからのイベント伝搬方式の問題 • イベントは全順序に並んでいないとうまくリードモデルを構築できない • ステートレスなアプリケーションでは、2層コミット問題とイベント追い 越し問題が起きる (参考文献) スターバックスは2フェーズコミットを使わない

    https://code.google.com/archive/p/gregors-ramblings-ja/wikis/18 _starbucks.wiki 最新状態を保存する DBとキューのトランザク ションを同一化しないと防ぐのが難しい。もしくは DB書き込み成功後にエンキューするプロセスを シャーディングする必要がある
  14. © Chatwork CDC(Change Data Capture)+Outbox方式 • CDCはWriteDBでの変更を捉える方式。WriteModelと同一TxのOutbox 内にドメインイベントを保存(一時保存でもよい)し下流に伝搬させる • DebeziumにはMySQLのbinlogを読む機能がある。DBのトランザクショ

    ンログはイベントと同等。結局Event Sourcingになっている
  15. © Chatwork FYI: AkkaのES方式 • トランザクションをActorが管理しているため、イベント列への書き込 みの整合性が保たれる。スナップショットがあれば、それより古いイベ ントは削除可能。 • ステートフルウェブアプリケーションになる

  16. © Chatwork まとめ • 単純にイベントを伝搬させるだけでは問題が生じる • ステートレスなウェブアプリケーションでは、 CDC+Outbox方式を採用したほうが無難 • CDC+Outbox方式は、トランザクションログが真の

    データソースでありESの一種であるという理解が必要 • 外形的にはCQRS+SSはできるように見えるが、真の データソースは最新データにはならない。結局はESに 帰結する
  17. © Chatwork ありがとうございました!

  18. None