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

リアクティブシステムとCQRS/ESで実現する Chatwork新アーキテクチャについて

リアクティブシステムとCQRS/ESで実現する Chatwork新アーキテクチャについて

ChatworkではリアクティブシステムとCQRS/ESを反映した次期アーキテクチャを構想しています。まだ構想段階ではありますが、なぜそれらを採用するのかメリット・デメリットも含めてご説明します。

かとじゅん

March 05, 2021
Tweet

More Decks by かとじゅん

Other Decks in Programming

Transcript

  1. 2021/03/05 第6回Reactive System Meetup in 西新宿
    Chatwork株式会社
    加藤潤一
    リアクティブシステムとCQRS/ESで実現する
    Chatwork新アーキテクチャについて

    View Slide

  2. 自己紹介
    ● 加藤潤一(かとじゅん)
    ○ @j5ik2o
    ● Chatwork テックリード
    ○ 仕事ではサグラダ・ファミリア的なプロダクト作ってます
    ● 最近やっていること
    ○ FP in ScalaをRustで写経(Propsは実装できた)
    ○ 匠真堂
    ■ 月一ラジオ番組やってます
    ● お知らせ
    ○ Chatworkが10周年迎えました
    ○ Chatwork Dev Day やります
    2

    View Slide

  3. アジェンダ
    1. Chatworkのアーキテクチャの変遷
    2. Chatworkのアーキテクチャ刷新の目的と手段
    ○ なぜリアクティブシステムか
    ○ なぜCQRSか
    ○ なぜEvent Sourcingか
    3. まとめ
    3

    View Slide

  4. Chatworkのアーキテクチャの変遷

    View Slide

  5. 2011年
    ● 2011/03/01 リリース
    5

    View Slide

  6. 2014〜2016年
    6

    View Slide

  7. アーキテクチャ刷新プロジェクト
    ● Falconプロジェクト(2014〜2016年)
    ○ メッセージ機能をCQRS+ESシステム(Akka, Kafka, HBase)としてリプレース
    ● Gaudiプロジェクト(2019年〜)
    ○ メッセージ機能以外も刷新することが目的
    ○ 技術検証と全体設計が完了
    ○ 2021年から開発を開始し社内で検証を開始する
    7

    View Slide

  8. Falconリリース(2016年末〜)
    ● RDS(MySQL)から新メッセージング基盤に移行
    Falcon

    View Slide

  9. マイクロサービス化(2018年〜)
    ● 左側がPHP, 右側がScala
    9

    View Slide

  10. Chatworkの
    アーキテクチャ刷新の目的と手段

    View Slide

  11. アーキテクチャ刷新の目的と手段
    ● 目的
    ○ 2025年の事業計画に合わせて生産性を維持・向上できるプロダクトと組織を
    構築する
    ● 手段
    ○ プロダクトの健全性を維持・向上させること
    ○ チームが独立して改善活動をおこなえること
    ○ 大規模なアジャイル開発が実践できること
    ○ いつでもレスポンスを返せる状態になっていること
    ■ リアクティブシステムを反映したアーキテクチャを実現する
    ■ CQRS/Event Sourcingシステムを実現する
    11
    本日話すところ

    View Slide

  12. リアクティブシステムへの移行(2022年〜)
    12

    View Slide

  13. FYI: 永続化アクターの概念
    13

    View Slide

  14. 補足:通常はリクエスト毎に Read-Modify-Write
    14

    View Slide

  15. なぜリアクティブシステムか

    View Slide

  16. 支える原理
    手段
    届けたい価値
    リアクティブシステムとは
    ● 即応性(Responsive)
    ○ システムは可能な限りすみやかに応答します
    ● 耐障害性(Resilient)
    ○ システムは障害に直⾯しても即応性を保ち続
    けます
    ● 弾力性(Elastic)
    ○ システムはワークロードが変動しても即応性
    を保ち続けます
    ● メッセージ駆動(Message-Driven)
    ○ リアクティブシステムは⾮同期・ノンブロッ
    キングなメッセージ・パッシングによってコ
    ンポーネント間の境界を確⽴します。 メッ
    セージ駆動は上記を実現するために利用され
    る手段
    ● 詳しくはこちらのブログ記事を参考にしてみてくだ
    さい
    16
    即応性(Responsive)
    メッセージ駆動(Message-driven)
    伸縮性(Elastic) 耐障害性(Resilient)

    View Slide

  17. 【FYI】リアクティブシステム ≠ リアクティブプログラミング
    ● 最近、リアクティブシステムとリアクティブプログラミングと
    いう用語に頻繁に遭遇します。よく誤解されていますが、これ
    らは等価ではありません。
    ● リアクティブシステムは、アーキテクチャレベルでリアクティ
    ブ原則を適用しています。リアクティブシステムを実現する手
    段としてFuture/Promise, Reactive Streams, アクターモデル
    などのリアクティブプログラミングが利用されます。だからと
    いって、自動的にリアクティブシステムになりません。
    ● 例えば、アプリケーションを1ノードだけにデプロイした場
    合、そのノードが故障したら全システムを失います。これでは
    リアクティブ宣言の耐障害性(回復力)がないので、リアクティ
    ブプログラミングを使っていても、リアクティブシステムでは
    ありません。
    17
    Node 1
    1台のみで運用。この 1台
    が故障すると全システム
    を失う

    View Slide

  18. なぜリアクティブシステムか
    ● リアクティブシステムは、どんな状況でも即応性があるシステムのこと
    ○ 簡単にいえば、「いつでも使えるシステム」を実現するための考え方のこと
    ● リアクティブ原則(The Reactive Principles)
    ○ 応答性を維持する/Stay Responsive
    ○ 不確実性を受入る/Accept Uncertainty
    ○ 失敗を受け入れる/Embrace Failure
    ○ 自律性を表明する/Assert Autonomy
    ○ 一貫性を調整する/Tailor Consistency
    ○ 時間を分離する/Decouple Time
    ○ 空間を分離する/Decouple Space
    ○ ダイナミクスを処理する/Handle Dynamics
    18

    View Slide

  19. 失敗を受け入れる/Embrace Failure
    ● expect things to go wrong and build for resilience
    ○ 物事がうまくいかないことを期待し、回復力のために構築する
    ● キーとなる考え方はBulkheading
    ○ Bulkheadingは船舶由来の用語です。大型貨物船の船倉は隔壁によって多くの区画に分割されています。船底
    が何らかの原因で破損した場合でも、影響を受けた区画だけが浸水し、他の区画は適切に密閉された状態を維
    持できるため浮力を維持できます。
    ○ 以下の図はReactive Design Patternsで紹介されています。
    19

    View Slide

  20. アクターモデルでどのようにBulkheadingするか
    20
    ● スーパーバイザであるコンポーネントは簡単に故
    障するような仕事はせずに、失敗しやすい仕事は
    ヒエラルキー下層の専門のコンポーネントに任せ
    る。このような構造を採用することで障害が発生
    しても、全体に障害が波及することを抑制する
    ● 障害発生時はスーパーバイザに判断を委任し、そ
    の指示に従ってコンポーネントを再起動して復旧
    する。 このような階層的な再起動を用いる障害処
    理によって、障害モデルを大幅に簡素化でき、予
    期しない障害に直面しても生き残る可能性が高め

    View Slide

  21. なぜCQRSなのか

    View Slide

  22. CQRSとは
    ● Command and Query Responsibility
    Segregation = コマンド・クエリ責務分離
    のこと。分離というより隔離という解釈が
    正しい
    ● コマンド(書き込み)とクエリ(読み込み)を
    スタックごとにそれぞれに隔離することを
    意味する。単にドメインモデルをコマンド
    用・クエリ用に分割することではない(理
    由はこちら参照)
    ● CQRSはDDDを前提としています(ドメイン
    から本質的ではないクエリ責務を排除する
    ための設計パターンです)。詳しくは
    CQRS Documents by Greg Young を参照
    のこと
    22

    View Slide

  23. CQRSイメージ図
    23

    View Slide

  24. なぜCQRSか
    ● コマンド側とクエリ側で以下のように要件がことなるため、コマンドとクエリをそ
    れぞれを隔離する
    24
    コマンド クエリ
    一貫性/可用性 トランザクション整合性を使い
    一貫性を重視する
    結果整合を使い可用性を重
    視する
    データ構造 トランザクション処理をおこな
    い正規化されたデータを保存
    することが好まれる(集約単
    位など)
    非正規化したデータ形式を取
    得することが好まれる (クライ
    アント都合のレスポンスなど )
    スケーラビリティ 全体のリクエスト比率とごく少
    数のトランザクション処理しか
    しない。必ずしもスケーラビリ
    ティは重要ではない
    全体のかなりのリクエスト比
    率を占める処理をおこなうた
    め、クエリ側はスケーラビリ
    ティが重要

    View Slide

  25. 「注文するコマンド要求」と「注文情報クエリ結果」の違い
    ● コマンド要求はシステムに送られるメッセージ
    ● クエリ結果はシステムから返されるメッセージ
    ○ 人間が確認する場合は非正規化されていることが多い
    注文ID
    注文日時
    商品ID
    注文数
    購入者ID
    注文ID
    注文日時
    商品ID
    商品名
    注文数
    購入者ID
    購入者名
    商品
    アカウント
    注文するコマンド要求 注文情報のクエリ結果

    View Slide

  26. CQRSではない場合の問題(1/2)
    ● クエリ要件を満たすことでリポジトリが複雑になる
    ○ 「クエリするだけでドメインロジックを呼び出さない」はドメインの責務らし
    くない。他にもページングやソートも扱うケースがある…。
    ● レスポンス用DTOをリポジトリで組み立てるため、N+1クエリが発生する
    ○ 本来RDBに任せるようなことをアプリケーション空間でやっている。リポジト
    リはそもそもこのような用途には向かない
    26
    val employees = employeeRepository.findByDeptIdsWithEmpNamePatterns(deptIds, empNamePatterns)
    // このあとに、ドメインロジックはない。
    DTOに詰め直してクライアントに返すだけ。
    val reservationDtos = reservationRepository.findByIds(ids) // SQL発行
    .map{ reservation =>
    val hotel = hotelRepository.findById(reservation.hotelId) // SQL発行
    val customer = cusotmerRepository.findById(reservation.customerId) // SQL発
    new ReservationDto(reservation, hotel.name, customer.name) // アプリケーション空間で結合及びデータを捨てる
    }

    View Slide

  27. CQRSではない場合の問題(2/2)
    ● コマンドを意識しないデータ指向では、エンドポイント、アプリケーションサービ
    ス、ドメインがCRUDの用語に汚染されてしまう、という仮説がある
    ○ 商品の注文=createPurchaseItem
    ○ 注文のキャンセル=updatePurchaseItem
    ● ドメインの動詞を重視するコマンド指向では、orderItem, cancelOrderなどユビキ
    タス言語にフォーカスできるようになる。コマンドの表現によって意図が明白なイ
    ンターフェイスを作ることができる
    ○ ただこの考え方は、CRUDであっても頑張れば可能…。
    ○ 実装というより分析の段階でコマンドを使うことのメリットが強い
    27

    View Slide

  28. CQRSの利点と欠点
    ● 利点
    ○ コマンドとクエリが分離しているため、耐障害性が高くなる(耐障害性に寄与
    する)。別々にデプロイできる
    ○ コマンドとクエリを必要に応じて個別に最適化できる(弾力性に寄与する)。
    別々にスケールさせることもできる
    ● 欠点
    ○ 非CQRSと比べてコストが掛かる。目的ごとにサブシステムを分けるので構成
    要素が多くなる
    ○ CQRSではC/Qごとにモデルが分離するため、単一モデルとしてシンプルだが
    ネットワーク全体としては複雑になる
    28

    View Slide

  29. なぜEvent Sourcingなのか

    View Slide

  30. Event Sourcingとは
    ● 唯一信頼できる情報源(Single Source Of Truth)は、状態(ステート)ではなく(ドメ
    イン)イベントという考え方
    ○ 従来からの最新状態を常に上書きするCRUDは、State Sourcingと呼ばれるこ
    とがある
    ● コマンドとクエリを統合するために使う
    30

    View Slide

  31. 【FYI】ドメインイベントとは
    ● イベントは過去に起きた出来事を意味する
    ● ドメインイベントは、ドメイン上のイベントを意味する
    ● 一般的には過去形の動詞として表現される
    ○ CargoShipped
    ○ CustomerRelocated
    ● イベントからコマンドが想起可能
    ○ ShipCargo
    ○ RelocateCustomer
    ● イベントとコマンドは似ているが別概念
    ○ コマンドは拒否されることがある
    ○ イベントはすでに起こったことを示す
    31

    View Slide

  32. なぜEvent Sourcingか
    ● CQRSはコマンド側からクエリ側に変更を伝える必要がある
    ○ コマンド側のドメインイベントをクエリ側に伝える
    ○ 場合によっては、コマンド側のDBに存在しない、ドメインの計算結果をクエリ側に伝える
    必要がある
    ● 上記の現実的な実現手段として以下がある
    ○ Event Sourcing
    ○ CDC + Outbox
    ■ アプリケーションではState Sourcingだが、ミドルウェアレベルではEvent
    Sourcingになっている
    ■ マイクロサービスのための分散データ 〜 イベントソーシング vs チェンジデータ
    キャプチャ
    ● 結局はEvent Sourcing以外に現実的な選択肢はない
    ○ 詳しくは CQRSはなぜEvent Sourcingになってしまうのか を参照
    32

    View Slide

  33. Event Sourcingの利点と欠点
    ● 利点
    ○ イベントは更新されず追記のみなので、スケーラビリティが確保しやすい
    ○ 特定の時点のリードモデルをイベントから導出することができる
    ○ ドメインイベントがあれば、リードモデルの設計をいつでもやり直せる
    ■ 実装コスト・データマイグレーションコストではゼロではないが
    ○ 監査ログや行動履歴の分析に利用することができる
    ■ 技術的に可能だが、GDPRなどの同意があるかは別問題
    ● 欠点
    ○ 大量のイベントから状態をリプレイする際に時間がかかる
    ■ スナップショットを使うとリプレイ時間を短縮できる
    ○ 原則的にすべてのイベントをストレージに保存する必要がある
    ■ スナップショット保存時に、古いイベントを消すことも可能
    33

    View Slide

  34. まとめ
    ● 「いつでも使える」を最初から考えるのは難しいので、手が出しにくい分野かもし
    れない…。逆説的にいえば、誰もが手を出しにくい分野だからこそ、希少性がでて
    くる
    ● ご時世的に、ウェブサービスやソフトウェアに対する期待は「いつでも使えて当た
    り前」になっていくかもしれない。リアクティブシステムへの需要がありそう?
    ● DDD, CQRS, Event Sourcing, アクターモデルなどの高度な技術も、リアクティブ
    のために必要と考えると、学ぶためのモチベーションになりやすい
    34

    View Slide

  35. 分散ストレージ・リライアビリティ
    エンジニア募集中

    View Slide

  36. 働くをもっと楽しく、創造的に

    View Slide