Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

2014〜2016年 6

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

なぜCQRSなのか

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

CQRSイメージ図 23

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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) // アプリケーション空間で結合及びデータを捨てる }

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

なぜEvent Sourcingなのか

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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