SlideShare a Scribd company logo
1 of 57
CQRS+EventSourcingをAkka Persistence
を使って実装してみる。〜コツとハマりポイン
ト〜
2016/03/16
Reactive Messaging Patternsプレ読書会 - CQRS、ESの基本を学ぶ
-
Satoshi Matsushita
自己紹介
• Satoshi Matsushita @satoshi_m8a
• Scala, Akka, DDD, フロントエンド, コンピュータビジョン, 機械学
習
• ゲヒルン株式会社
Python, Go, Erlang, Scala, OCaml, TypeScript
「Gehirn Infrastructure Services」
セキュリティ診断
• ECのシステムをAkka Persistenceを使って開発していた。
Akka + DDD 気運の高まり(1)
• Scala Matsuri 2016 二日目アンカンファレンス
DDD+CQRS+EventSourcing実装する会
(Akkaパフォーマンスチューニングについて話してみよう会)
by かとじゅんさん(@j5ik2o)
Akka + DDD 気運の高まり(2)
• Vaughn Vernon 氏の書籍
Akka + DDD 気運の高まり(3)
• Lightbendの一貫したツールキット
• DDDを意識したもの
Akka Persistence
Akka Persistence Query
Akka + DDD 気運の高まり(4)
• Lagom
マイクロサービスを構築するためのフレームワーク
CQRS+ESがベースになっている
Akka + DDD 気運の高まり(5)
• マイクロサービス化の流れ
• リアクティブという考え方の広まり
目次
• CQRS
• イベントソーシング
• コマンドサイド
• クエリサイド
• 参考
CQRS
Command Query Responsibility Segregation
コマンド・クエリ責務分離
よくある階層化パターン
プレゼンテーション層
アプリケーション層
ドメイン層
インフラストラクチャ層
CQRS概念図
プレゼンテーション層
アプリケーション層
ドメイン層
インフラストラクチャ層
データアクセス層
コマンドサイド クエリサイド
ドメインモデル
• 例:Twitterのフォロワー / フォロイー
ユーザー
フォロワーのリスト
フォロイーのリスト
ブロックリスト
ドメインモデル
• フォローするという振る舞いに着目すると
ユーザー
フォローする(userId)
ブロックされる(userId)
フォロイーのリスト
ブロックされてい
るユーザーのリス
ト
CQRS
ユーザー
フォロイーのリスト
ブロックされている
ユーザーのリスト
コマンドサイド クエリサイド
フォロワーのリスト
フォロイーのリスト
ブロックされている
ユーザーのリスト
ブロックしている
ユーザーのリスト
…
ユーザーのリスト
複雑さに立ち向かう
• 複雑なドメインを、そのまま複雑なドメインモデル
に落として満足しがち
• まずは、コンテキスト分割を検討
• ドメインをよく観察し、振る舞いにフォーカスする
• CQRSやESの検討はそのあと
Event Sourcing
Event Sourcing
カートID : “cart1”
商品: “A”->0, “B”->1
カート作成
商品Aを追加
商品Bを追加
商品Aを削除
カートID : “cart1”
商品: “A”->1, “B”->0
Snapshot
1
2
Snapshot
101
100
• 全てのイベントを初めから復元してい
ては時間がかかる
• スナップショットをとって途中から復
元
CQRS+ES
コマンドサイド クエリサイド
Journal
Aggregate Root
Command Service
Projection DAO
Query Service
DB DB
Command
Domain Event
Domain Event
DTO
DTO
Polling
データベース選択のポイント
• コマンドサイド
・Cassandra, DynamoDB, Riak
・書き込みをスケールできるもの、可用性の高いものが良
い
• クエリサイド
・各種RDB, NoSQL(ドキュメント指向・グラフ指向)
・クエリに強いものが良い
・組み合わせOK
Materialized View Pattern
• コマンドサイドのDBが正のデータを保持する、
クエリサイドはそれのView
• リードレプリカの構築(読み込みをスケール)
https://msdn.microsoft.com/ja-jp/library/dn589782.aspx から引用
サービス統合も容易
• 新しいサービスを追加したら、ドメインイベントを流
し込む。
• あたかも、その新しいサービスが最初から統合されて
るかのように振る舞う。
• 現在のイベントまで追いついたら、システムに馴染ん
でいる。
結果整合性
コマンドサイド クエリサイド
Journal
Aggregate Root
Command Service
Projection DAO
Query Service
DB DB
Command
Domain Event
Domain Event
DTO
DTO
Polling
Over Kill
• 例:ID、名前、パスワード、E-Mailアドレスを持つ、
会員AR
• パスワードやE-Mailアドレスの変更履歴を追うことで
、ビジネスの価値を生むのか?
• CQRSだけ、もしくは単純なCRUDができるだけでよ
いのでは?
余談:純粋なREST APIはDDDに向かない
• REST APIで一旦少なくなった情報を復元するのは困難
• 純粋なRESTにこだわらない。
CQRSで作った折角のリッチなコマンドモデルが意味をなさなくなる
。
業務で発生する操作
情報量:大
REST API
情報量:小
リッチなコマンドモデル
情報量:大
> <
×
ESのメリット・デメリット
• メリット
インピーダンスミスマッチがない。
履歴管理が不要、データ解析やデバッグにも使える。
イベントは追記のみなのでパフォーマンスが良い。
機能追加も容易。
• デメリット
イベントの修正が煩雑(後述)
データサイズの問題
CQRS+ESのメリット・デメリット
• メリット
ドメインの振る舞いが明確になる
Viewを柔軟につくれる
スケールも柔軟に
• デメリット
結果整合性
Akkaで作る CQRS+ES
コマンドサイド クエリサイド
Journal
Aggregate Root
Command Service
Projection DAO
Query Service
DB DB
Command
Domain Event
Domain Event
DTO
DTO
Polling
Akka
Persistence
Akka Persistence
Plugin
Akka Persistence
Query
Slick3
Akka Cluster
Sharding
コマンドサイド
Akka Persistence
• Actorの内部状態を永続化することができる
• AkkaのCQRSとイベントソーシングに使われる
• メッセージの再送の仕組みも提供(At least once delivery)
例:カウントするActor
• CounUpコマンドを受け取り、内部のカウントを増
加させていく。
PersistentActor
Persistent
Actor
Journal
persistenceId = “c100”
count = 0
CountUp
CountIncreased
Ack(永続化完了)
• コマンドを受け付け、ドメインイベントを発行する。
①
② ③
PersistentActor
Persistent
Actor
Journal
persistenceId = “c100”
count = 1
Ack
④
Ack
⑤
• JournalからのAckを待ち、内部状態を更新する
ポイント
• 内部状態(count)の更新はドメインイベントの永続化
完了を待ってから行う
• 永続化されていないイベントは起こっていないイベ
ントと同義
PersistentActorの復元
• クラッシュ、タイムアウト時の停止、シャードの移
動など様々な理由でActorは再起動する。
• 再起動したActorを元の状態に戻し、コマンドを受
け付けたい。
PersistentActorの復元
Persistent
Actor
Journal
persistenceId = “c100”
count = 3
CountIncreased
① ②
CountIncreased
CountIncreased
Select Events where
persistenceId = “c100”
③
Akka Persistence
class CountUpActor extends PersistentActor {
override def persistenceId: String = self.path.name
context.setReceiveTimeout(120.seconds)
var count: Int = 0
def updateState(event: Increased) = {
this.count = this.count + event.amount
}
override def receiveRecover: Receive = {
case e: Increased =>
updateState(e)
}
override def receiveCommand: Receive = {
case c: CountUp =>
persist(Increased(c.amount)) {
event =>
updateState(event)
sender() ! event
}
case ReceiveTimeout =>
context.parent ! Passivate(stopMessage = Stop)
case Stop =>
context.stop(self)
}
}
<- ここで永続化
<- 永続化が終わった後に状態を更新
<- 復元したイベントを元に状態を更新
ポイント
• Recoveryが完了するまで、コマンドを処理しないよ
うになっている。
• Recovery時は内部状態の更新だけを行う、
外部へコマンドやメッセージを発行してはならない
。
Aggregate Root
• 実際はPersistentActorを継承して、AggregateRoot
アクターを作ると良い。(c.f. akka-ddd)
https://github.com/pawelkaczor/akka-ddd/blob/master/akka-ddd-
core/src/main/scala/pl/newicom/dddd/aggregate/AggregateRoot.scala
• スナップショット操作, GracefulPassivation,
リカバリを隠蔽
ドメインイベントの設計
• ドメインイベントは起こった事実を表す。
イベント名は過去形 (Increased, Decresed, Created)
• 「住所を変更しました」 vs 「引っ越しました」
• 「旧システムからデータを移行しました」イベント
• きっかけとなったコマンドをイベントのメタデータとして保持することも
• 粒度は細かすぎても良くない。
e.g.「郵便番号を変更しました」
ドメインイベントのシリアライズ
• ドメインイベントはシリアライズされて、
コマンドサイドのDBに保存される。
• デフォルトではJavaのシリアライザが使われる
• Javaのシリアライザは速度面でも、拡張面でも問題がある
• 実運用するのであれば、 Google Protocol Buffers が無難
ドメインイベントのスキーマ変更
• フィールドを追加したり、一つのイベントを分割など
• EventAdapterを使ったり、一応の解決方法はあるが煩雑
• Stamina
https://github.com/scalapenos/stamina
Persistence Plugin
• Cassandra, JDBC, DynamoDB, Riak 向けのPlugin
• テスト用のInMemory Pluginや LevelDB Plugin
• ReadJournal API(後述)の実装しやすいDBがおすすめ
• Cassandra PluginはAkka公式
クエリサイド
Akka Persistence Query
• CQRSのクエリサイドの実装に使われる
• クエリサイド全体ではなく、
Journalからクエリ側のDBへの投影に使われる
• experimental (Akka 2.4.2)
Pluginも出揃っていない
Journal Projection DAO
DB
Domain Event DTO
Polling
クエリサイド
DTO
• Read Journal APIを実装したPersistence Plugin を使う
• JournalをPollingして、ドメインイベントを待ち受ける
ReadJournal API
• EventsByTagQuery
タグを元にイベントを取得
• EventsByPersistenceIdQuery
PersistenceIdを元にイベントを取得
• AllPersistenceIdsQuery
すべてのPersistenceIdを取得
• CurrentPersistenceIdsQuery
現在存在する全てのPersistenceIdを取得(ポーリングなし)
• すべてのJournal Pluginがこれら実装しているわけではない
実装が困難なものもあるので、Journal用のDB選びは慎重に
イベントにタグを付与する
class ThreadEventAdapter extends WriteEventAdapter {
override def manifest(event: Any): String = ""
val tags = Set("Thread")
override def toJournal(event: Any): Any = event match {
case e: ThreadEvent =>
Tagged(event, tags)
case _ =>
event
}
}
Projection
• Read Model Projection / Read Model Updaterともいう
• ドメインイベントを元に、Viewを構築する
Projection
val readJournal = PersistenceQuery(system)
.readJournalFor[LeveldbReadJournal](LeveldbReadJournal.Identifier)
implicit val mat = ActorMaterializer()(system)
val dao = new ThreadsDao(dbConfig)
val projection = new ThreadProjection(dao)
readJournal
.eventsByTag("Thread", projection.lastOffset)
.mapAsync(1) { envelope =>
projection.update(envelope.event).map(_ => envelope.offset)
}
.mapAsync(1) { offset => projection.saveProgress(offset) }
.runWith(Sink.ignore)
クエリ
• Slick3などを使ってクエリする。
その他
• Process Manager
複数のAggregate Rootにまたがった処理を順序
良く実行する
PersistentFSMを使う。
• Cluster Sharding
Aggregate Rootを分散させる。
Cluster Singleton
まとめ
• CQRS+ESのコマンドサイドとクエリサイドを
Akka Persistenceと Akka Persistence Queryで実装
した
• Lagom
参考
• CQRS Journey
https://msdn.microsoft.com/ja-jp/library/jj554200.aspx
• .NETのエンタープライズアプリケーションアーキテクチャ
• 実践ドメイン駆動設計
Reactive Messaging Patterns
with the Actor Model 読書会
興味のある方はお声がけください。
ありがとうございました

More Related Content

What's hot

What's hot (20)

マイクロサービス化デザインパターン - #AWSDevDay Tokyo 2018
マイクロサービス化デザインパターン - #AWSDevDay Tokyo 2018マイクロサービス化デザインパターン - #AWSDevDay Tokyo 2018
マイクロサービス化デザインパターン - #AWSDevDay Tokyo 2018
 
JVMのGCアルゴリズムとチューニング
JVMのGCアルゴリズムとチューニングJVMのGCアルゴリズムとチューニング
JVMのGCアルゴリズムとチューニング
 
ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)
 
At least onceってぶっちゃけ問題の先送りだったよね #kafkajp
At least onceってぶっちゃけ問題の先送りだったよね #kafkajpAt least onceってぶっちゃけ問題の先送りだったよね #kafkajp
At least onceってぶっちゃけ問題の先送りだったよね #kafkajp
 
マルチテナントのアプリケーション実装〜実践編〜
マルチテナントのアプリケーション実装〜実践編〜マルチテナントのアプリケーション実装〜実践編〜
マルチテナントのアプリケーション実装〜実践編〜
 
PostgreSQLアンチパターン
PostgreSQLアンチパターンPostgreSQLアンチパターン
PostgreSQLアンチパターン
 
ドメイン駆動設計に15年取り組んでわかったこと
ドメイン駆動設計に15年取り組んでわかったことドメイン駆動設計に15年取り組んでわかったこと
ドメイン駆動設計に15年取り組んでわかったこと
 
マルチテナント化で知っておきたいデータベースのこと
マルチテナント化で知っておきたいデータベースのことマルチテナント化で知っておきたいデータベースのこと
マルチテナント化で知っておきたいデータベースのこと
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
 
人生がときめくAPIテスト自動化 with Karate
人生がときめくAPIテスト自動化 with Karate人生がときめくAPIテスト自動化 with Karate
人生がときめくAPIテスト自動化 with Karate
 
Swaggerでのapi開発よもやま話
Swaggerでのapi開発よもやま話Swaggerでのapi開発よもやま話
Swaggerでのapi開発よもやま話
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!
 
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション
 
ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説ドメイン駆動設計サンプルコードの徹底解説
ドメイン駆動設計サンプルコードの徹底解説
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
 
Fluentdのお勧めシステム構成パターン
Fluentdのお勧めシステム構成パターンFluentdのお勧めシステム構成パターン
Fluentdのお勧めシステム構成パターン
 
What's new in Spring Boot 2.6 ?
What's new in Spring Boot 2.6 ?What's new in Spring Boot 2.6 ?
What's new in Spring Boot 2.6 ?
 
マイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDay
マイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDayマイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDay
マイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDay
 

Similar to CQRS+ESをAkka Persistenceを使って実装してみる。

これからのコンピューティングの変化とJava-JJUG CCC 2015 Fall
これからのコンピューティングの変化とJava-JJUG CCC 2015 Fallこれからのコンピューティングの変化とJava-JJUG CCC 2015 Fall
これからのコンピューティングの変化とJava-JJUG CCC 2015 Fall
なおき きしだ
 
Perl 非同期プログラミング
Perl 非同期プログラミングPerl 非同期プログラミング
Perl 非同期プログラミング
lestrrat
 
20120405 setsunaセミナー
20120405 setsunaセミナー20120405 setsunaセミナー
20120405 setsunaセミナー
Takahiro Iwase
 
Web Operations and Perl kansai.pm#14
Web Operations and Perl kansai.pm#14Web Operations and Perl kansai.pm#14
Web Operations and Perl kansai.pm#14
Masahiro Nagano
 
分散メモリ環境におけるシェルスクリプトの高速化手法の提案
分散メモリ環境におけるシェルスクリプトの高速化手法の提案分散メモリ環境におけるシェルスクリプトの高速化手法の提案
分散メモリ環境におけるシェルスクリプトの高速化手法の提案
Keisuke Umeno
 
Zabbixでvmc statsの情報など監視
Zabbixでvmc statsの情報など監視Zabbixでvmc statsの情報など監視
Zabbixでvmc statsの情報など監視
Shota Onishi
 

Similar to CQRS+ESをAkka Persistenceを使って実装してみる。 (20)

WebRTC getStats - WebRTC Meetup Tokyo 5 LT
WebRTC getStats - WebRTC Meetup Tokyo 5 LTWebRTC getStats - WebRTC Meetup Tokyo 5 LT
WebRTC getStats - WebRTC Meetup Tokyo 5 LT
 
Apache geode at-s1p
Apache geode at-s1pApache geode at-s1p
Apache geode at-s1p
 
20120317 IT系勉強会 in 神戸
20120317 IT系勉強会 in 神戸20120317 IT系勉強会 in 神戸
20120317 IT系勉強会 in 神戸
 
MoteMote Compiler Plugin
MoteMote Compiler PluginMoteMote Compiler Plugin
MoteMote Compiler Plugin
 
GKEで半年運用してみた
GKEで半年運用してみたGKEで半年運用してみた
GKEで半年運用してみた
 
これからのコンピューティングの変化とJava-JJUG CCC 2015 Fall
これからのコンピューティングの変化とJava-JJUG CCC 2015 Fallこれからのコンピューティングの変化とJava-JJUG CCC 2015 Fall
これからのコンピューティングの変化とJava-JJUG CCC 2015 Fall
 
Arctic.js
Arctic.jsArctic.js
Arctic.js
 
20190625 OpenACC 講習会 第2部
20190625 OpenACC 講習会 第2部20190625 OpenACC 講習会 第2部
20190625 OpenACC 講習会 第2部
 
Perl 非同期プログラミング
Perl 非同期プログラミングPerl 非同期プログラミング
Perl 非同期プログラミング
 
React Native GUIDE
React Native GUIDEReact Native GUIDE
React Native GUIDE
 
20120405 setsunaセミナー
20120405 setsunaセミナー20120405 setsunaセミナー
20120405 setsunaセミナー
 
Real world android akka
Real world android akkaReal world android akka
Real world android akka
 
GMOインターネットにおけるOpenStack Swiftのサービス化とその利用事例のご紹介 - OpenStack最新情報セミナー 2015年2月
GMOインターネットにおけるOpenStack Swiftのサービス化とその利用事例のご紹介 - OpenStack最新情報セミナー 2015年2月GMOインターネットにおけるOpenStack Swiftのサービス化とその利用事例のご紹介 - OpenStack最新情報セミナー 2015年2月
GMOインターネットにおけるOpenStack Swiftのサービス化とその利用事例のご紹介 - OpenStack最新情報セミナー 2015年2月
 
Spark Streaming Snippets
Spark Streaming SnippetsSpark Streaming Snippets
Spark Streaming Snippets
 
No SSH (@nojima; KMC関東例会)
No SSH (@nojima; KMC関東例会)No SSH (@nojima; KMC関東例会)
No SSH (@nojima; KMC関東例会)
 
Web Operations and Perl kansai.pm#14
Web Operations and Perl kansai.pm#14Web Operations and Perl kansai.pm#14
Web Operations and Perl kansai.pm#14
 
Architecting on Alibaba Cloud - Fundamentals - 2018
Architecting on Alibaba Cloud - Fundamentals - 2018Architecting on Alibaba Cloud - Fundamentals - 2018
Architecting on Alibaba Cloud - Fundamentals - 2018
 
Open stackceilometer
Open stackceilometerOpen stackceilometer
Open stackceilometer
 
分散メモリ環境におけるシェルスクリプトの高速化手法の提案
分散メモリ環境におけるシェルスクリプトの高速化手法の提案分散メモリ環境におけるシェルスクリプトの高速化手法の提案
分散メモリ環境におけるシェルスクリプトの高速化手法の提案
 
Zabbixでvmc statsの情報など監視
Zabbixでvmc statsの情報など監視Zabbixでvmc statsの情報など監視
Zabbixでvmc statsの情報など監視
 

CQRS+ESをAkka Persistenceを使って実装してみる。

Editor's Notes

  1. EC のシステムをAkka Persistenceを使って開発していた。 今日は、そのときの、経験を元に CQRS+ESのシステムの作り方やコツやハマりどころを紹介したいと思います。
  2. AkkaやDDDはに興味をもつ人たちが確実に増えてきている。
  3. ヴァンバーノン氏の2つの書籍
  4. Lightbendから最近公開されたLagom、 マイクロサービスを構築するためのフレームワーク  CQRS+ESがベースになっている Lagomがなにをやっているか分かる人は、今日の話は退屈かもしれないのです。 実際に、ECのシステムを開発している時に感じたのは、 CQRS+ESを使えば、ドメインモデリングに集中することができ、 他の部分は作業として片付けることができるし、フレームワーク化しちゃえばもっと楽だな って思っていたら実際に作られてましたw ドメインモデリングに集中できる時代が近づいてきたという感じです。 Lightbendに確実にロックインされるのは悔しいですが、選択肢としてはありだと思います。
  5. Lagomなどの、マイクロサービス化の流れや、 リアクティブという考え方の広まりも受けて AkkaとDDDを組み合わせて使う気運が高まってきたと感じます。
  6. (言語やテクノロジに依存しないパターンやアプローチである。)
  7. ドメイン層に登場するモデル(ドメインモデル、単にモデル、WriteModelともいったり) データアクセス層に登場するモデル (DTOといったり、ReadModelといったり)
  8. ユーザーは他のユーザーをフォローする。 ユーザーはブロックしているユーザーにフォローされてはならない。 UIに引きずられがちなドメインモデリングの例 フォローする、ブロックするという課題を解決することにフォーカスしていない。
  9. まず、フォローするという振る舞いに注目すると、フォロワーのリストは不要。 「ブロックリスト」から「ブロックされているユーザーのリスト」へ変更。 そして、フォローするという振る舞いの中で、 自分がだれによってブロックされているかをチェックして、ブロックされていなければフォロイーのリストへ追加。 ここで脱線して、フォローするという振る舞いの中で、フォローするユーザーの「ブロックしている」リストを問い合わせてチェックもできるが、シンプルではなくなる・動くモデルではあるが、「解決する」モデルではなくなる。 んー、でも、フォロワーのリストも、フォロイーのリストも、ブッロクされてる・しているリストも欲しいんですけど。
  10. ドメインモデルをコマンドサイドとクエリサイドに分ける。 コマンドサイドにはフォロワーのリストが登場しない。(フォローするという振る舞いに影響しない) 代わりにクエリサイドに登場している。 クエリサイドはUIやプレゼンテーション層の要求に合わせて、好きなようにクエリを発行して、ReadModelを構築する。 これは単純な例なのでこのような間違いはしないかもしれないですが、UIに引きずられて、そのままモデリングして、 自分のフォロイーのリストに相手を追加して、なおかつ、相手のフォロワーのリストに自分を追加するような無駄に複雑なモデリングをしていませんか? この例だと、フォロワーのリストはフォローするという振る舞いに影響しないはずです。 これは、.NETのエンタプライズアプリケーションアーキテクチャからの引用なのですが、 CQRSを使わなかった場合のドメインモデルの複雑さは N×N になって CQRSを使った場合は コマンドサイドとクエリサイドで分かれて N + N になって、 複雑さの爆発をかなり抑制することができると思います。
  11. 複雑なドメインを、そのまま複雑なドメインモデルに落として満足しがち。 我々は、複雑さと戦っている。そのままだと意味が無い。 複雑なドメインをシンプルなモデルに シンプルにする手段として、まずはコンテキスト分割、そのあとCQRSやESを検討 振る舞いやドメインを捉えきれていないのにCQRSから始めると失敗するかもしれません。 (ユーザーに購買履歴がぶら下がっているモデルが、ログイン認証につかうパスワードをもってたりしませんか?)
  12. (カートに発生するイベントの説明) 例えば、カートにクーポンコードが紐づくように業務が拡張されたら、 「クーポンコードが追加された」イベントを作るだけで、簡単に拡張できます。 DBのテーブルスキーマの変更も不要で、ORMもいらなくなります。
  13. - コマンドとイベントの流れの説明 Projeciton - データベース - データベースの選択
  14. Cluster Shardingなどで  分散させて、ドメインイベントを大量に書き込むため、書き込みをスケールできるものがよい。 また、一つや2つDBのノードが落ちても書き込めるように可用性が高いものがよい。 CAP定理でいうところのAが強いAPのあたり。A(Avialavility)P(Patrition Toralance) のあたり、 クエリサイドは 要件に合わせてなんでも良い。 クエリに強いものが良い 組み合わせも自由 要件を満たせるならコマンドサイドと同じDBでも良い。 例えばCassandra は CQLというクエリ言語も一応使えるのでそれを使っても良い。実際、Lagomは コマンドサイドもクエリサイドもCassandraになっている。
  15. コマンドサイドのDBが正のデータを保持し、クエリサイドはそれのViewと捉えることができます。 このようなパターンをMaterialized View Patternといい、 CQRSはこのパターンの応用。 クエリサイドのDBはぶっ飛んでしまってもOK, コマンドサイドのDBが残っていればゼロから作れる。 また、読み込みをスケールさせたい場合のMongoDBのようなリードレプリカの構築と同じことができて、 ただし、通常のリードレプリカと違って、種となるDBとリードレプリカ用のDBは異なるものが選択できる。 例えば、MongoならマスタもレプリカもMongoですが、CQRS+ESを使えば、マスタはCassandraでリードレプリカはMySQL+ Neo4j みたいなこともできる。 さらには、DBが提供するレプリケーションの機能も不要になります。 また、CQRSを使えばコマンドサイドとクエリサイドを独立してスケールさせることができます。
  16. Pollingの仕組みを使って、新しく追加したサービスにドメインイベントを流し込む サービスバスも不要になるという利点があります。
  17. コマンドを実行した直後に、 クエリサイドからクエリを発行して、コマンドで更新した結果を取得しようとしても、 上手く取得できないと思います。PollingやクエリサイドのDB反映もコマンドと同期されて実行されるわけではないので、 コマンドで成功ACKが返ってきても、クエリサイドのDBには反映が終わっていない可能性が高いです。 時間がたてば、解消されるのですが、このような状況を結果整合性といいます。 UIで反映されているように上手くごまかすのが無難な解決方法です。 コマンドサイドを無理やりクエリのように使うこともできますが、パフォーマンスの問題や、CQRSが意味を成さなくなる問題があります。 結果整合性でも問題ないと判断できる場合もあります。 例えば、ある会員登録のプロセスで、一意な会員IDとは別に、メールアドレスを入力しないといけないが、そのメールアドレスは他の人と被ってっはいけないという状況で、 Queryサイドに反映済みであれば良いのですが、例えば二人の会員が別々の場所で、同じメールアドレスを入力するとタイミングによっては同時にバリデーションが成功してしまうと思います。 一つの解決方法は、会員登録のプロセスをクラスタをまたいだシングルトンに管理させて(すなわち、クラスタシングルトンのProcess Manager)、そいつは会員登録プロセスを1つずつ実行し、直近に登録されたメールアドレスリストを保持している。そのリストに含まれていたらバリデーションエラーで弾く方法が考えらますが、単一障害点になったり、なによりも無駄に煩雑です。 よくよく考えてみると、クエリサイドに反映されるまでの数ミリ秒の間に同時に同じメールアドレスを思いついて入力する人が世界中にどれだけいるか? を考えてみると、たいして問題にならないケースがあります。問題になるケースもあるので、ビジネスのインパクトに合わせてトレードオフになると思います。 もちろん、コマンドよりも手前でQueryサイドのEmailListを元にバリデーションしているので、過去に登録済みのメールアドレスとの重複チェックはできます。問題になるのはクエリサイドに反映される数ミリ秒の間です。
  18. イベント列から状態の復元が基本なので、RDBに起因するインピーダンスミスマッチがない。 ORMで悩まない。
  19. Materialised View PatternによってViewが柔軟に作れる。
  20. REST APIはAkka HTTP, レガシーシステムとの統合はAkka AMQP
  21. 流れの説明 内部状態に基づく条件判定は①この段階でやる。 例:countが100以上だったら、別のイベントを発行する。
  22. senderにイベントを返す、 ACK[Unit]を返すか ACK[DomainEvent] を返すか。
  23. 「バグで間違った処理をしてしまったので取り消しました」イベント ドメインイベントの設計は、ドメイン自体を理解しないとできないから難しいのは当たり前。
  24. Stamina イベントのバージョン管理を手助けしてくれる。 ScalaMatsuriの応募セッションでもStaminaのセッションがあったが、落選してた。
  25. (スライドの説明) EventsByTagQueryを使うためには、Journalに書き込む前にタグを付与する必要があります。 それが、次のスライドの。。。
  26. これは個人的に作っている、Redditのクローンのサンプルコードなのですが、 WriterEventAdapterを使って、”Thread” というタグをドメインイベントに付与して、 Taggedイベントを作成し、それをJournalに保存します。 CQRSを目的とするのであれば、通常はAggregateRoot名(ここでいうThread)をタグとして付与します。 Cassandra Journal プラグインではタグ数は最大3個が上限になっているので、タグのつけすぎにご注意ください。 どのイベントにどのAdapterを使うかはapplication.confで設定できます。
  27. イベントにタグがついたのでそれをReadJournalで取得して、Viewを構築します。
  28. まずはReadJournalを取得、ここではLeveldbReadJournalを取得、AkkaのInMemoryプラグインはReadJournalにはまだ対応していないので、 ProjectionのテストではLevelDBしか使えない。 ReadJournalはAkka Streamがベースになっているので、マテリアライザをセットする。 ・記録してどのイベントまで投影できたかをoffset を使って記録する。 projection.updateでeventをDTOに変換して保存
  29. CQRS+ESのコマンドサイドとクエリサイドを Akka Persistenceと Akka Persistence Queryで実装した. experimental
  30. RMP: サブシステムやマイクロサービスをどのようなメッセージングパターンで統合していくかという説明に重きが置かれている。 ClusterShardingやAkka Persistence、ProcessManagerの話題も。