Slide 1

Slide 1 text

リアクティブ・アーキテクチャ 基礎講座 かとじゅん( ) 2020/07/16 @j5ik2o 1

Slide 2

Slide 2 text

誰? Chatwork社でテックリード 最近の活動 2020-04-15 2020-05-26 以下のコースの認定を取得しました @j5ik2o Chatworkテックリードが“今”の⾃分に集中してきた理由。 Scala×DDDに出会い、サービス改善に⽣かすまで - Findy Engineer Lab ドメイン駆動設計をわかりやすく - ドメインのモデル設計を⼿を動 かしながら学ぼう - エンジニアHub Lightbend Academy 2

Slide 3

Slide 3 text

アジェンダ のコースで解説されている、リアクティブ・アーキテクチャの 概要を解説します 具体的なイメージがつきやすいように、リアクティブアーキテクチャのために設計さ れたツールキットであるAkkaも⼀緒に紹介します Lightbend Academy 3

Slide 4

Slide 4 text

なぜリアクティブか 第⼀⽬標は常に使えるソフトウェアを提供すること 4

Slide 5

Slide 5 text

こんな状況になったら… 仕事で使う重要なサービスで 数時間停⽌したら… レスポンスタイムが、30秒以上掛かったら… 応答性のないソフトウェアは、ユーザーの不満につながる 5

Slide 6

Slide 6 text

代替サービスへの乗換リスク 遅いソフトウェアや利⽤できないソフトウェアはユーザの効率を下げている 提供側がミッションクリティカルでないと判断しても、ユーザには関係ない ユーザはすぐによりよい他の選択肢を⾒つける。選択肢は無数にある時代、すぐに乗 り換えられてしまう 6

Slide 7

Slide 7 text

⾮機能要求は変化している ノード数は数百・数千に レスポンスタイムはミリ秒オーダーに ダウンタイムは限りなく0時間に データ規模はペタバイトに 技術的理由ではなく、ユーザ要求の変化に対応することが⽬的 7

Slide 8

Slide 8 text

リアクティブ原則 2014年に公開された原則。16200名が署名 Scala/Akkaで有名な、Lightbend社のCTO Jonas Bonér⽒がFirst Author 形 ⼿段 値 即応性 拡張可能 拡張可能 耐障害性 メッセージ駆動 弾⼒性 http://www.reactivemanifesto.org/ 8

Slide 9

Slide 9 text

リアクティブ原則 即応性(Responsive) システムは可能な限り速やかに応答する 耐障害性(Resilient) システムは障害に直⾯しても即応性を保ち続ける 弾⼒性(Elastic) システムはワークロードが変動しても即応性を保ち続ける メッセージ駆動(Message-Driven) コンポーネント間を⾮同期にメッセージを送受信する通信スタイル(メッセージパ ッシング) 9

Slide 10

Slide 10 text

即応性(Responsive) システムは可能な限りすみやかに応答する。 同時に複数ユーザから重なり合うリアルタイムなリクエストがあっても、障害に直⾯し ても、応答性は維持される。 10

Slide 11

Slide 11 text

耐障害性(Resilient) システムは障害に直⾯しても即応性を保ち続ける。 障害はそれぞれのコンポーネントに封じ込められ、コンポーネントは互いに隔離される ので、システムが部分的に故障してもシステム全体を危険に晒すことなしに回復するこ とが保証される 11

Slide 12

Slide 12 text

弾⼒性(Elastic) システムはワークロードが変動しても即応性を保ち続ける。 ワークロードに連動してリソースを増減させ、シャーディングやレプリケーションされ たコンポーネント間でワークロードを分散する 12

Slide 13

Slide 13 text

メッセージ駆動(Message-Driven) リアクティブシステムは⾮同期なメッセージパッシングに依ってコンポーネント間の境 界を確⽴する。 13

Slide 14

Slide 14 text

⾮同期・ノンブロッキングなメッセージパッシング 受信側はアクティブ時のみリソースを消費できるのでシステムのオーバヘッドを抑制 できる Actor Actor ①メッセージを送信 ②Dispathcer がプッシュする ③タスクを実⾏する Mailbox メソッド駆動の場合は呼出先が完了するまで呼出元は解放されず、他のタスクを実⾏ できない。メッセージパッシングの場合はコマンドを送信したらすぐ返答を待たずに 別のタスクを実⾏(Fire And Forget)し、呼出先から返答がきたときに続きを⾏うた め、スケーラビリティが向上する Actor Actor ①コマンドを送信 ③コマンドを送信 ②タスクを実⾏ ②リプライを待た ずに次のタスクへ ④タスクを実⾏ Method Method ①メソッドをコール ③メソッドコールのリターン ②タスクを実⾏ ④次のタスクへ 14

Slide 15

Slide 15 text

位置透過性と透過的リモーティング 位置透過性は、アクターの位置情報がローカルであってもリモートのように⾒せるこ と。メッセージ送信側は、送信先のすべてがリモートに⾒える 透過的リモーティングは、リモートからの呼出をローカルからの呼出に⾒せること。 メッセージ受信側は、送信元のすべてがローカルに⾒える マルチスレッディングとRPCを、メッセージ駆動という⼀つのプログラミングモデル で扱えるようになる // ┝戚jパ もハかZぇおてwそおjノテ ガげぬdくヅェを わぶ想情wは groupChatRef1 ! AddMessage("Hello World!") object HelloWorld { final case class Greet(whom: String, replyTo: ActorRef[Greeted]) final case class Greeted(whom: String, from: ActorRef[Greet]) // パ もハiね肩し輯sばぃぬdく訣yは def apply(): Behavior[Greet] = Behaviors.receive { (context, message) => context.log.info("Hello {}!", message.whom) message.replyTo ! Greeted(message.whom, context.self) Behaviors.same } } 15

Slide 16

Slide 16 text

Supervision 監督者を仲介することで故障の隔離が可能となる。呼出元のアクターは⼦アクターが 故障したことも知らない Actor Supervisor ①メッセージを送信 ③タスクを実⾏する 故障した⼦Actor 再⽣成された⼦Actor ②作り直される - ⼦アクターの障害管理 と存在を隠蔽 - メッセージブローカー としての役割を持つ 通知 16

Slide 17

Slide 17 text

リアクティブシステム vs リアクティブプログラミング 17

Slide 18

Slide 18 text

リアクティブシステム vs リアクティブプログラミング リアクティブシステム アーキテクチャレベルで、リアクティブ原則を適⽤する リアクティブプログラミング リアクティブシステムを効率よく実装するための⼿段。しかし、リアクティブプ ログラミングを使ったからと⾔ってリアクティブシステムになるわけではない 例えば⾮同期・ノンブロッキングなプログラミングで実装したコンポーネント を、1つのノード上で稼働させた場合、耐障害性や弾⼒性がないのでリアクティ ブシステムではない 18

Slide 19

Slide 19 text

リアクティブ・マイクロサービス 19

Slide 20

Slide 20 text

モノリス vs マイクロサービス の スペクトラム ほとんどのシステムが中間に位置している Application A B C D A B C D Application A B C D A B C D A B C D 20

Slide 21

Slide 21 text

モノリス 21

Slide 22

Slide 22 text

FYI: モノリスとは アプリケーションに明確な分離がないもの 最悪なシナリオは泥団⼦。アプリケーションの全てが他の全てに依存している 理解することも修正することも難しい 22

Slide 23

Slide 23 text

FYI: モノリスの特徴 単⼀ユニットとしてデプロイされる 単⼀の共有されたデータベース 同期化されたメソッド呼び出しに依存 コンポーネントはデータベース結合 ビックバン・リリース 改善のサイクルタイムが⻑くなる 複製のスケーリング 23

Slide 24

Slide 24 text

FYI: モノリスのメリット リファクタリングが簡単 クロスモジュールのリファクタリングが可能 ⼀貫性の維持が簡単 データベースが⼀貫性の境界になる 単⼀のデプロイプロセス 1⼀つのものをデプロイすればプロセスが完了 モニタリング対象が⼀つ モノリスだけを監視すればよい ⽐較的シンプルなスケーラビリティモデル プロセスのコピーをデプロイするだけでよい 24

Slide 25

Slide 25 text

モノリスのデメリット 25

Slide 26

Slide 26 text

パフォーマンスの問題 パフォーマンスがマシンの上限に依存する モノリスのサイズに⽐例してリソースも消費する スケールアップしかできないので、サーバコストが割⾼になる傾向 26

Slide 27

Slide 27 text

スケーラビリティの問題 データベースが許す限りのスケーラビリティしかない DB(Writer)はシングルインスタンスで拡張ができない 不要なコンポーネントも⼀緒にスケールするためリソース効率が悪い 27

Slide 28

Slide 28 text

耐障害性の問題 カスケード障害が起きやすい 1つのコンポーネントが障害を起こすと、他の部分は負荷を分配できないため、 モノリス全体がダウンすることがある クラウド思考にありがちな「障害に直⾯したら再起動する」は代償が伴うことも ある(実⾏環境によってJITプロファイル結果を捨ててしまうこともあるなど)。ま た、Auto Scalingはフォローアップであることを忘れてはならない。再起動は最 終⼿段、それまでにアプリケーションレベルでできることがある。 28

Slide 29

Slide 29 text

FYI: カスケード障害の例 クラスタB が消失 通常の負荷分散 クラスタA が過負荷状態に 出典:「オライリー社 SRE サイトリライアビリティエンジニアリング」より 29

Slide 30

Slide 30 text

モノリスへの処⽅箋 ドメインの境界に沿ってサービスを分割すること。 ECサイト 予約 注⽂ 出荷 マイクロサービスへ近づいていくが、レガシーなモノリスからの移⾏は⾮常に難し い…。 30

Slide 31

Slide 31 text

マイクロサービス 31

Slide 32

Slide 32 text

FYI: マイクロサービスの特徴 ⾃律性 特殊化 出典: マイクロサービスの概要 | AWS 32

Slide 33

Slide 33 text

FYI: マイクロサービスのメリット 俊敏性 柔軟性のあるスケーリング 容易なデプロイ 技術的な⾃由 再利⽤可能なコード 耐障害性 出典: マイクロサービスの概要 | AWS 33

Slide 34

Slide 34 text

リアクティブ・マイクロサービスにおける 隔離の原則(Principles of Isolation) 34

Slide 35

Slide 35 text

隔離の原則(Principles of Isolation) システムから 「状態(State)」 「空間(Space)」 「時間(Time)」 「失敗(Failure)」 を隔離する原則 35

Slide 36

Slide 36 text

状態の隔離 マイクロサービスの状態へのアクセスはAPIを経由しなければならない データベースへの直接アクセスは禁⽌されている APIを維持すれば、内部の改善は独⽴して進めることができる 36

Slide 37

Slide 37 text

空間の隔離 マイクロサービスは、他のマイクロサービスがどこにデプロイされているか気にすべ きではない。 どこのマシンでもデータセンターであってもよい 需要に応じてサービスをスケールアップしたりスケールダウンしたりできる 37

Slide 38

Slide 38 text

時間の隔離 マイクロサービスはお互いに待つべきではない。リクエストは⾮同期・ノンブロッキ ングであることが理想 リクエストを送信後に応答を待つ間にスレッドをブロックせずにその場を⽴ち去る (Fire and Forget) スレッドを占拠せず、スレッドが⾃由になり資源をより効率的に使うことができるよ うになる 時間が経てば最終的に⼀貫性が成り⽴ち、スケーラビリティが向上する 38

Slide 39

Slide 39 text

故障の隔離 あるサービスで故障が発⽣しても、他のサービスは影響を受けずに稼働し続けること ができる 依存関係: 注⽂サービス→顧客サービス では、顧客サービスが故障すると、注⽂サー ビスも失敗してしまう。それに依存しないようにする 39

Slide 40

Slide 40 text

隔離の技術 (1/2) Bulkheading ソフトウェアに区画を設けて全体障害に発展しないようにする アクターモデルはError Kernel(Supervisionによって故障した箇所を切り離し正 常な部分を保護する), let-it-crash(故障したときは最初からすべてをリセットして やりやおす)パターンを利⽤して実現 サービスレベルでは境界づけられたコンテキスト単位で区切る Circuit Breaker リトライ先のサービスが過負荷にならないようにするための仕組み 40

Slide 41

Slide 41 text

隔離の技術 (2/2) メッセージ駆動アーキテクチャ ⾮同期・ノンブロッキングは時間と故障を分離することができる ⾃律性 各サービスは⾃分⾃⾝の動作しか保証できない。サービス同⼠が互いに依存しあ って動作している場合、全体として脆弱になってしまう ゲートウェイサービス マイクロサービスのデメリットの1つとして、クライアントアプリケーションに 複雑さをもたらすことがある API Gateway, BFFなどをつかってこのギャップを解消する 41

Slide 42

Slide 42 text

分散システムにおけるスケーラビリティ 42

Slide 43

Slide 43 text

スケーラビリティと パフォーマンス パフォーマンス改善 レイテンシを最適化すること 0時間に近づくことはできるが、けっして0時間にはできない ユーザビリティの観点から⼗分であればよい スケーラビリティ改善 スループットを最適化すること 理論的には限界がないが、通常はソフトウェアの設計やコストなどの問題によっ て制限される リアクティブアーキテクチャでは、理論的限界のないスケーラビリティを追求する 43

Slide 44

Slide 44 text

分散システムでの⼀貫性 結果整合性(Eventual Consistency) 強い⼀貫性(Strong Consistency) 44

Slide 45

Slide 45 text

結果整合性 Eventual Consistency データの更新がなければ、あるデータへの全てのアクセスが最終的には最新の値を返 すようになること データの更新がある間は、整合性は保証されない。つまり、データの整合性を保証す るには、⼀定の期間 データの更新を⽌めなければならない 45

Slide 46

Slide 46 text

結果整合性のサブセット 因果⼀貫性 / Causal Consistency 因果関係のある項⽬が、共通した順序で処理されること 逐次⼀貫性 / Sequencial Consistency 因果関係に関係なく、すべての項⽬が共通した順序で処理されること 並列処理できないのでスケーラビリティが犠牲になる これらの⼀貫性にはそれぞれトレードオフがあることを認識して選択する。 46

Slide 47

Slide 47 text

強い⼀貫性(Strong Consistency) 強い⼀貫性とはデータが⾒えるようになる前に、すべてのコンポーネントからの同意 を得ること 分散システムにおいては情報を転送するにはタイムラグがあるため、強い⼀貫性をそ のまま実現できないのでエミュレートする データを⼀箇所でロック(部分的な⾮分散システム)すれば⼀貫性を持たせること ができる ロックにはトレードオフがあり、パフォーマンス悪化や柔軟性やスケーラビリテ ィが犠牲になる 47

Slide 48

Slide 48 text

競合の影響 同じリソースを2つ以上のコンポーネントから利⽤すると競合が発⽣し、⼀⽅がリソ ースを開放するまで他⽅は待つことになる レイテンシが悪化し待ち⾏列が⻑くなるとスケーラビリティに⼤きな制限を与える 48

Slide 49

Slide 49 text

アムダールの法則 Amdahl's Law システムにおいて、並列化できる部分とできない部分がある。並列化によって改善す ることができるのはそれが可能な部分だけであり、並列化できない部分は改善できな い 競合は並列化を制限し、改善できる部分を減少させる 並列処理A 並列処理B 並列処理C 並列処理F 並列処理G 並列処理H 直列処理D 直列処理E 49

Slide 50

Slide 50 text

コヒーレンシ遅延 複数のノード間で互いに状態を伝え合い、最終的にすべてのノードの状態が⼀致する ようになるまでの時間が遅延すること 合意を取らなければならないコンポーネントの数が多くなると、合意が指数関数的に 困難になる ヒト ヒト ヒト ヒト 6 ヒト ヒト ヒト 3 ヒト ヒト 1 コンポーネント数に対して通信チャネルの数は指数関数的に増える 50

Slide 51

Slide 51 text

ガンザーの法則 Gunther's Universal Scalability Law システムをスケールさせるためコンポーネント数を増やしていくと、コンポーネント間 の状態調整にかかるコストがスケールアウトによる改善を上回ってしまい、むしろ逆効 果になること。 スケールアウトの規模 ( コンポーネントの数) コンポーネント間の調整コスト 51

Slide 52

Slide 52 text

コヒーレンシ遅延の対策 コーディネーターを配置して、全員が他の全員と合意を取らないようにする この⽅法のリスク 集約されている情報が最新とは限らない コーディネーターが単⼀障害点やボトルネックになりやすい ヒト ヒト ヒト ヒト 6 ヒト ヒト ヒト ヒト 3 調整役を導⼊すればコヒーレン シ遅延を軽減できるが、調整役 はSPoF になってしまう… 。 52

Slide 53

Slide 53 text

リアクティブ・アーキテクチャのアプローチ 状態はシェアードナッシング できるだけロックを排除 ロックするならできるだけ⼩さく ブロッキングしない 結果整合性を有効活⽤ コンポーネントの⾃律性を⾼める 53

Slide 54

Slide 54 text

シャーディングとは ⼀貫性とスケーラビリティのバランスを取るために競合を分離すること。シャーディ ングは競合を分離するテクニック データベースのI/Oシャーディングのことではない アプリケーションが扱う整合性を確保する単位(通常はドメインの集約単位)ごとに分 割すること アプリケーションレベルで実装するので、データベースの種類に関係なく実現できる アプリケーションとデータベース間の通信を減らすことができ、パフォーマンスが向 上する 54

Slide 55

Slide 55 text

Akka Cluster Sharding とは 軽量プロセスであるActorを分散システム上でシャーディングするためのモジュール メッセージはShardRegion → Shard → Entityのようにルーティングされる ShadはEntity(集約)のグループ。Entityのスーパーバイザ。Entityを作成する ShardRegionは各ノードで開始される。ShardCoorinatorにShardの場所を要求す る。Shardを⼦アクターとして作成する ShardCoordinatorは調整役。どのShardRegionがどのShardを所有するかを決定 し、ShardRegionに指⽰する。リバランス時はメッセージが移動先のShardRegion にバッファリングされ移動後に移動先のShardに配信される 55

Slide 56

Slide 56 text

実装例 Counter Entity(Aggregate) ShardRegion object Counter { sealed trait Command case object Increment extends Command final case class GetValue(replyTo: ActorRef[Int]) extends Command def apply(entityId: String): Behavior[Command] = { def updated(value: Int): Behavior[Command] = { Behaviors.receiveMessage[Command] { case Increment => updated(value + 1) case GetValue(replyTo) => replyTo ! value Behaviors.same } } updated(0) } } val TypeKey = EntityTypeKey[Counter.Command]("Counter") // ShardRegionげ粛巌押 val shardRegion: ActorRef[ShardingEnvelope[Counter.Command]] = sharding.init(Entity(TypeKey)(createBehavior = entityContext => Counter(entityContext.entityId))) // ShardRegionくヅェを わぶ想は shardRegion ! ShardingEnvelope("counter-1", Counter.Increment) 56

Slide 57

Slide 57 text

⼀貫性とスケーラビリティのバランス あるEntity(集約)IDは物理的に1つの場所にしか存在しない。これによって、分散シ ステム上の複数のノードで状態の整合性を保つ必要がなくなる Shardを経由するメッセージは順序が保証される。Entity(集約)のキーには集約のID が適している。Shard間で偏りが⽣じないようにID値は分散される必要がある ShardCoordinatorは競合になり得るが、⾮常に⼩さなタスクしかしない。リバラン スしない限りルート情報はキャッシュされる。多くのノードにShardを分散させるこ とでスケーラビリティを確保できる 57

Slide 58

Slide 58 text

シャーディングされたアクターはSSOTとなる Entityでデータベースに書き込んでからキャッシュすることでデータベースと同期し ていることを保証できる。キャッシュが失われていなければデータベースから読み出 す必要がなくなる。データベースは常に読み書きが発⽣する場所でなく、キャッシュ が失われた場合に復元するためのバックアップになる。 真のデータソースはデータベースではなく、Entity(集約)の中の状態となる。 Entity(集約)は唯⼀信頼できる情報源(SSOT = Single Source Of Truth)。これまでの ステートレスなウェブアプリケーションとは全く違うプログラミングモデルとなる 副作⽤を起こすコマンド 副作⽤を起こさないコマンド 副作⽤を伴うビジネスロジック キャッシュのバックアップ エンティティ( 集約) アクター 58

Slide 59

Slide 59 text

ネットワーク分断時に⼀貫性か可⽤性を選ぶか CAP定理はそのまま解釈するとミスリードの原因に。完全に排他的選択ではなくバラ ンスが必要。ネットワーク分断時に、⼀貫性を選ぶのか、可能性を選ぶのかという選 択になる シャーディングは⼀貫性を重視しているので、ネットワーク分断が発⽣した場合は可 ⽤性が犠牲になるが、SPoFがないので全体障害にはならない。 Sprit Brain Resolver(Akka2.6.6から標準搭載)はノードが失われたことを検知し て短時間に復旧させることができる。 ネットワーク分断のときに可⽤性を選択したい場合はCRDT(Conflict-free Replicated Data Type)を使うことができる。実装としてはAkka Distributed Data ⼀貫性を取るか可⽤性を取るかの選択は、技術的な判断ではなくビジネス上の判断で ある。⼀貫性か可⽤性化は0か1かではなく、システムの部分部分で求められるビジ ネス要件に応じて選択する。ビジネスにとって、⼀貫性が保証されないことのインパ クトのほうが⼤きいのか、利⽤できない時間が発⽣することのほうが問題なのかを、 ビジネス側と議論するべき 59

Slide 60

Slide 60 text

CQRS & Event Sourcing 60

Slide 61

Slide 61 text

Command and Query Responsibility Segregation コマンド・クエリ責務分離(分離という より隔離に近いイメージ) 2010年 Greg Young⽒が考案したパタ ーン。 1997年にBertrand Meyer⽒が考案したコ マンドクエリ分離原則(Command-Query Separation:CQS)をアーキテクチャに適⽤ したものがCQRS。 「あらゆるメソッドは、アクションを実 ⾏するコマンドか、呼び出し元にデータ を返すクエリかのいずれかであって、両 ⽅を⾏ってはならない。これは、質問を することで回答を変化させてはならない ということだ。」 CQRSとは 61

Slide 62

Slide 62 text

なぜCQRSか そもそもコマンドとクエリの要件に⾮対称性がある Command Query ⼀貫性 結果整合性よりトランザクショ ン整合性を扱うこと多い ほとんどの場合結果整合を使う データ形式 トランザクション処理を⾏い正 規化されたデータを保存するこ とが好まれる(集約単位など) ⾮正規化したデータ形式を取得 することが好まれる(クライント 都合のレスポンスなど) スケーラビリ ティ 全体のリクエスト⽐率とごく少 数のトランザクション処理しか しない。必ずしもスケーラビリ ティは重要ではない 全体のかなりのリクエスト⽐率 を占める処理を⾏うため、クエ リ側はスケーラビリティが重要 検索・レポートやトランザクション処理を両⽴する単⼀モデルの実現は困難 62

Slide 63

Slide 63 text

過去にに発⽣した出来事のこと=イベント ドメイン上のアクティビティを⽰す ⼀般的には過去形の動詞で表現される CustomerRelocated CargoShipped イベントからコマンドを想起できる RelocateCustomer ShipCargo イベントとコマンドは似ているが、⼈間が 扱う⾔語としては別モノ コマンドは拒否されることがある イベントは既に起こったことを⽰す FYI: ドメインイベントとは 63

Slide 64

Slide 64 text

イベントソーシング イベントソーシングでは、状態ではなくドメインイベントを永続化する。ドメインイ ベントを履歴として扱われる。 最新の状態は、ドメインイベントをリプレイすることによって得ることができる。 CQRS と ES はそれぞれ個別に利⽤できる⼿法だが⼀般的に組み合わせて使われること が多く、組み合わせることでより弾⼒性や回復⼒のあるシステムを構築できるようにな る 64

Slide 65

Slide 65 text

永続化アクター(Akka Persistence) 副作⽤を起こすコマンド 副作⽤を起こさないコマンド ①コマンドハンドラ ドメインイベントが永続化される ジャーナルDB エンティティ( 集約) アクター ②ドメインイベントの永続化 ③イベントハンドラ スナップショットの永続化 ドメインイベントN 件ごとに スナップショットを永続化 アクターがリプレ イ時にSnapshot 以降のイベントが 読み込まれる アクターがリプレイ時に最新の Snapshot があれば読み込まれる 副作⽤が起きると きだけDB にロック なしでイベントが 追記される スナップショットDB 副作⽤を起こすコマンドが受理されるとドメインイベントへの追記保存とメモリ上の 状態遷移が起きる。副作⽤が起きないコマンドはメモリ状態からリプライを返す。コ マンドを処理するために、DBからの読み込みは⼀切不要(パフォーマンスに有利) 不要になった永続化アクターはいつでも破棄できる。再び必要ならば永続化されてい るドメインイベントを基にリプレイすることができる。リプレイするドメインイベン トが⼤量な場合はスナップショットと組み合わせることでリプレイ時間を短縮するこ とができる。 65

Slide 66

Slide 66 text

実装例 persistenceIdは集約IDを指定する Stateには、ドメインオブジェクトを格納する コマンドの処理はコマンドハンドラでイベントの処理はイベントハンドラで⾏う sealed trait Command final case class Add(data: String) extends Command case object Clear extends Command sealed trait Event final case class Added(data: String) extends Event case object Cleared extends Event final case class State(history: List[String] = Nil) def apply(id: String): Behavior[Command] = EventSourcedBehavior[Command, Event, State]( persistenceId = PersistenceId.ofUniqueId(id), // 綬耗ID emptyState = State(Nil), // 綬耗げ訟尊 commandHandler, // りヂプキコプキネ eventHandler) // ほソプガコプキネ private val commandHandler: (State, Command) => Effect[Event, State] = { (state, command) => command match { case Add(data) => Effect.persist(Added(data)) case Clear => Effect.persist(Cleared) } } private val eventHandler: (State, Event) => State = { (state, event) => event match { case Added(data) => state.copy((data :: state.history).take(5)) case Cleared => State(Nil) } } 66

Slide 67

Slide 67 text

CQRS & Event Sourcing の Pros/Cons コマンドとクエリに分割することで、コンポーネントが増え、構築や運⽤にかかるコ ストが増⼤するが、それぞれの⽬的に合わせて最適化できる コマンドのドメインモデルとクエリのリードモデルに分割することで、⼀つ⼀つはモ デルがシンプルになる コマンド側とクエリ側は別々にスケールさせることができる データベースは増えるが、個々の要件に応じたデータベースを選択できるようになる クエリ要件に基づいたデータ設計を後付けでできる Writeモデルはシャーディングと組み合わせて⼀貫性を保証するが、ネットワーク分 断時に可⽤性を犠牲にしなければならないことがある(ただし、SPoFを回避してリカ バリ可能) 結果整合性のReadモデルは可⽤性を確保しやすい Event Sourcing 監査ログの要件に対応できる ある時点の状態に戻すことができる。問題の調査に使える 不正な状態を修正できる。git revertのように イベントの追記しか発⽣しないため、データベースの処理効率が良い 67

Slide 68

Slide 68 text

FYI: CQRS & ES with Akka Cluster on AWS akka-cluster ReadModel Updater Event Router DynamoDB Streams DynamoDB Read DB ing pod pod pod svc svc pod pod pod pod pod pod pod pod pod Kafka transfer domain events Read API Write API 可⽤性重視 ⼀貫性重視 機能的には問題なし。パフォーマンスとスケーラビリティは知⾒が溜まり次第共有予定 68

Slide 69

Slide 69 text

まとめ ⽇常の仕事を⽀えるようなサービスでは「常に使えるソフトウェアを実現する」が MUSTになりがち。提供側がミッションクリティカルでないと判断しても顧客側には 関係ない。競合のサービスに簡単に乗り換えられるリスクがある 「常に使えるソフトウェアを実現する」の実現⼿段として、「リアクティブ・アーキ テクチャ」の考え⽅は⾮常に有益。⼀⽅で、それなりに仕組みや⼿間が必要。実際の 現場では、それだけのコストを掛けるに値するシステムであるかよく考える必要があ る エンジニア個⼈として選択肢を増やす意味で、リアクティブ・アーキテクチャの考え ⽅と経験を⾝につけて、バランスが取れるようになるとよいかもしれない (7⽉末まで無償) お勧めです Lightbend Academy 69

Slide 70

Slide 70 text

おしまい 70