SlideShare a Scribd company logo
1 of 110
Download to read offline
copyright Fringe81 Co.,Ltd.
Ladder of CQRS+ES
@mtoyoshi
2017.9.9
copyright Fringe81 Co.,Ltd.
自己紹介
@mtoyoshi
Fringe81から来ました
Scala歴3年と少しになりました
iPad + ApplePencilが最近のお気に入り
copyright Fringe81 Co.,Ltd.
[PR]
copyright Fringe81 Co.,Ltd.
今日の話のテーマ:
思想・定義の話より現場感(リアル)を
どういう課題感から導入に至ったのか
自分が感じていた疑問や失敗の話などを中心に
小さく始めるとどういうところから?
小さくはじめた場合その先には
どういう事を考慮したらいい世界が待っている?
copyright Fringe81 Co.,Ltd.
なお、登壇に際して色々読みましたが
「CQRS Journey」が一番参考になりました
https://msdn.microsoft.com/ja-jp/library/jj554200.aspx
copyright Fringe81 Co.,Ltd.
長年の悩み
copyright Fringe81 Co.,Ltd.
プロジェクト途中から
モデルの凝集度が下がりだす
個人的経験では
レポート機能きっかけでなりやすい
DB側も正規化崩しなど
TABLEの制約・定義も汚染されてくる
copyright Fringe81 Co.,Ltd.
自分の技術力が
まだまだ足りないからだと思っていた
(理想vs現実の落とし所がヘタ?)
copyright Fringe81 Co.,Ltd.
It is not possible to create an optimal solution
for searching,reporting,and processing transactions
utilizing a single model.
-CQRS documents by Greg Young-
copyright Fringe81 Co.,Ltd.
copyright Fringe81 Co.,Ltd.
Command
Query
Responsibility
Segregation
〜コマンド・クエリ責務分離〜
copyright Fringe81 Co.,Ltd.
Command(Write) Query(Read)
アクセス量の
占める割合
少 多
スケール性 △ ◎
複雑さ ビジネスロジック 多データと応答速度
DB傾向 正規化 非正規化
copyright Fringe81 Co.,Ltd.
新しいサービス立ち上げの
チャンスが巡ってきた
CQRSアーキテクチャを導入
※当初はEventSourcingはやるつもりなし
copyright Fringe81 Co.,Ltd.
■構成:
C側はCleanArchitecture + DDD
Q側はController + DAO
※実行環境はGCP
※DBはcloud datastore
 (クセ強め)
copyright Fringe81 Co.,Ltd.
さっそくですが
CQRSアーキテクチャを導入してみて
どうだったの?
copyright Fringe81 Co.,Ltd.
モデルの凝集度が下がる問題は
今のところ問題なし
導入のねらいは成功
(DDDに素直に取り組める)
copyright Fringe81 Co.,Ltd.
〜嬉しい発見〜
生産性の観点では
レビューにメリハリ
C側(のdomain層)に集中
copyright Fringe81 Co.,Ltd.
ところで
CとQでDB分けたの?
copyright Fringe81 Co.,Ltd.
自分たちは
基本は同一テーブルを使う※が
最適化したいところだけQ用を用意
※スケール性は?
cloud datastoreは分散DBで
R/W負荷に応じてオートスケールする
copyright Fringe81 Co.,Ltd.
・分けずに同一DB参照する
copyright Fringe81 Co.,Ltd.
・master/slave構成でCはmasterをQはslaveを
・分けずに同一DB参照する
copyright Fringe81 Co.,Ltd.
・C専用DBとQ専用DBを分ける
・master/slave構成でCはmasterをQはslaveを
・分けずに同一DB参照する
※DBまで含めた最適化が出来る
copyright Fringe81 Co.,Ltd.
・C専用DBとQ専用DBを分ける
・master/slave構成でCはmasterをQはslaveを
・分けずに同一DB参照する
【一貫性】
一般的に
複数台に分散すると
強い一貫性が失われ
結果整合性(eventual concistency)
を考慮する必要がある
copyright Fringe81 Co.,Ltd.
・C専用DBとQ専用DBを分ける
・master/slave構成でCはmasterをQはslaveを
・分けずに同一DB参照する
どうやってC側からQ側を作るの?
copyright Fringe81 Co.,Ltd.
C Q
copyright Fringe81 Co.,Ltd.
C Q
別サービス
別クエリ用
copyright Fringe81 Co.,Ltd.
C Q
別サービス
INSERT
や
UPDATE
集約用
レコード
Event
query1用
レコード
query2用
レコード
publish
EventBus
subscriber
copyright Fringe81 Co.,Ltd.
C Q
別サービス
INSERT
&
UPDATE
集約用
レコード
Event
query1用
レコード
query2用
レコード
publish
EventBus
subscriber
Event:
発生した事象をあらわす
命名は過去形
copyright Fringe81 Co.,Ltd.
C Q
別サービス
INSERT
&
UPDATE
集約用
レコード
Event
query1用
レコード
query2用
レコード
publish
EventBus
subscriber
※ちなみにですが
これはEventSourcingでは
ありません(後ほど)
copyright Fringe81 Co.,Ltd.
val (updatedAggregate, event) =
aggregate.executeXxx(arg1, arg2)
repository.store(updatedAggregate)
eventBusPublisher.run(event)
copyright Fringe81 Co.,Ltd.
さらにLadderをのぼる
copyright Fringe81 Co.,Ltd.
・集約の永続化
・Eventのpublish
一方が失敗した場合、非一貫状態になる
copyright Fringe81 Co.,Ltd.
それはそうだけど
publishに失敗するなんて・・?
そこまで考慮する?
copyright Fringe81 Co.,Ltd.
「Eventのpublishに失敗」
経験しました
copyright Fringe81 Co.,Ltd.
クラウドサービスへの過信
障害復旧後にログから
手動リカバリする辛さ・・
レジリエンス考慮していれば!
copyright Fringe81 Co.,Ltd.
ではどうやって?
参考:CQRS jornery
copyright Fringe81 Co.,Ltd.
・集約の永続化
・Eventのpublish
一方が失敗した場合、非一貫状態になる
この2つは別々実行ではなく
同時に行われるようにしたい
copyright Fringe81 Co.,Ltd.
val (updatedAggregate, event) =
aggregate.executeXxx(arg1, arg2)
repository.store(updatedAggregate)
eventBusPublisher.run(event)
val updatedAggregate =
aggregate.executeXxx(arg1, arg2)
repository.store(updatedAggregate)
リポジトリ内からイベントパブリッシュ
(集約からイベントを取得)
copyright Fringe81 Co.,Ltd.
val (updatedAggregate, event) =
aggregate.executeXxx(arg1, arg2)
repository.store(updatedAggregate)
eventBusPublisher.run(event)
val updatedAggregate =
aggregate.executeXxx(arg1, arg2)
repository.store(updatedAggregate)
アトミックにしたいがDB保存とイベントpublishのTx
を同一にするのは難しい・・
copyright Fringe81 Co.,Ltd.
・集約の永続化
・Eventのpublish
・集約の永続化
・publish待ちレコードとして永続化
※2件保存するということ
※定期的にpublish待ちレコード群を読み込んでEventのpublish
※成功したらこれらレコード群を削除
※サービス落ちても復帰時にレコードを参照して途中のものの
publishを継続
copyright Fringe81 Co.,Ltd.
publish成功後にレコード削除に失敗したら
再送してしまう問題
copyright Fringe81 Co.,Ltd.
publish成功後にレコード削除に失敗したら
再送してしまう問題
案1:at-least-onceだし誤って再送するもやむなし
copyright Fringe81 Co.,Ltd.
publish成功後にレコード削除に失敗したら
再送してしまう問題
案1:at-least-onceだし誤って再送するもやむなし
案2:利用しているメッセージサービス側に重複を検知
する仕組みがないか調べる
 → GCPの場合、Cloud PubSubだが、調べてみると無かった
ただし、Cloud PubSubとCloud Dataflowを組み合わせることで重複
を検知できる
参考:https://cloud.google.com/dataflow/model/pubsub-io
copyright Fringe81 Co.,Ltd.
ちなみにakkaの場合、この問題はどう取り扱うのだろう?
akkaというよりは応用的な話だからLagom※が参考になるかな
※akka,playを基盤としたリアクティブなmicroservice構築のためのフレームワーク
参考:https://www.lagomframework.com/documentation/latest/java/PubSub.html
copyright Fringe81 Co.,Ltd.
CQRSまとめ
・レジリエンスの考慮
・集約保存 & イベントpublish
・DBをCとQに分離
・プログラムの世界をCとQに分離
copyright Fringe81 Co.,Ltd.
さらにLadderをのぼる
copyright Fringe81 Co.,Ltd.
C Q
別サービス
INSERT
や
UPDATE
集約用
レコード
Event
query1用
レコード
query2用
レコード
publish
EventBus
subscriber
copyright Fringe81 Co.,Ltd.
C Q
別サービス
INSERT
&
UPDATE
集約用
レコード
Event
query1用
レコード
query2用
レコード
publish
EventBus
subscriber
もしここの部分が
サービスの最初からあるわけではなくて
途中から必要になったとしたら・・・?
copyright Fringe81 Co.,Ltd.
どうやったら
作れるだろう?
copyright Fringe81 Co.,Ltd.
最新状態
C Q
C側DBから作れ・・・ないだろう
updated_atは最終更新日を表すだけ
現在に至るまで何回更新されたのか?など情報は不足
INSERT
&
UPDATE
copyright Fringe81 Co.,Ltd.
publishしたEvent群と
同じものが取得できれば
copyright Fringe81 Co.,Ltd.
Event1
Event2
Event3
Event4
Event5
つまりEventが
永続化されていて
いつでも取り出せたら
Q
copyright Fringe81 Co.,Ltd.
value: 8
Event1
Event2
Event3
Event4
Event5
value:8
※DBから復元したScalaオブジェクト
copyright Fringe81 Co.,Ltd.
Event1
Event2
Event3
Event4
Event5
value:8
Added: 3
Added: 6
Deleted: 4
Deleted: 1
Added: 4
copyright Fringe81 Co.,Ltd.
Event1
Event2
Event3
Event4
Event5
value:8
Added: 3
Added: 6
Deleted: 4
Deleted: 1
Added: 4
Eventは
append-only
copyright Fringe81 Co.,Ltd.
Event1
Event2
Event3
Event4
Event5
Event Sourcing
copyright Fringe81 Co.,Ltd.
C Q
別サービス
INSERT
や
UPDATE
集約用
レコード
Event
query1用
レコード
query2用
レコード
publish
EventBus
subscriber
集約の永続化
集約変更Eventのpublish
集約変更Eventの永続化
集約変更Eventのpublish
copyright Fringe81 Co.,Ltd.
EventSourcingのメリット
と言われているもの
copyright Fringe81 Co.,Ltd.
メリット 要約
Performance ESにおいては永続化はイベントの append-only。
UPDATE処理のパフォーマンス改善が望める。
Simplification factとしてのイベントを保存するだけ。
O-R間のインピーダンスミスマッチが起きない。
Audit trail システムで何が起きてその状態なのかの追跡が可能となり、監査ログとし
て使える
Integration with
other
subsystems
疎結合を促す。
また、他のサブシステムへ publishした全てのイベントは eventStoreに保存
されている。
Deriving
additional
business value
from the event
history
将来起こるビジネス要件を予想することは困難だが全てのイベント履歴を
持っていることで対処可能性が高まる。
例:ある時点での状態がどうであったかにこたえられるなど
Production
troubleshooting
本番環境のデータをテスト環境にコピーするなどして状態の再現を簡単に
できるためトラブルシューティングに役立つ
Fixing Errors ESの場合factとしてイベントが保存されているだけなので、コーディングエ
ラーを修正するだけで DB値を手動メンテせずにすむ場合がある
Testing テスト容易性が高まる
Flexibility イベント群があればいかなる構造のデータにも変換可能
参考:CQRS jornery(Introducing Event Sourcing)より
copyright Fringe81 Co.,Ltd.
メリット 要約
Performance ESにおいては永続化はイベントの append-only。
UPDATE処理のパフォーマンス改善が望める。
Simplification factとしてのイベントを保存するだけ。
O-R間のインピーダンスミスマッチが起きない。
Audit trail システムで何が起きてその状態なのかの追跡が可能となり、監査ログとし
て使える
Integration with
other
subsystems
疎結合を促す。
また、他のサブシステムへ publishした全てのイベントは eventStoreに保存
されている。
Deriving
additional
business value
from the event
history
将来起こるビジネス要件を予想することは困難だが全てのイベント履歴を
持っていることで対処可能性が高まる。
例:ある時点での状態がどうであったかにこたえられるなど
Production
troubleshooting
本番環境のデータをテスト環境にコピーするなどして状態の再現を簡単に
できるためトラブルシューティングに役立つ
Fixing Errors ESの場合factとしてイベントが保存されているだけなので、コーディングエ
ラーを修正するだけで DB値を手動メンテせずにすむ場合がある
Testing テスト容易性が高まる
Flexibility イベント群があればいかなる構造のデータにも変換可能
参考:CQRS jornery(Introducing Event Sourcing)より
イベント群があれば
いかなる構造のデータにも
変換可能
copyright Fringe81 Co.,Ltd.
メリット 要約
Performance ESにおいては永続化はイベントの append-only。
UPDATE処理のパフォーマンス改善が望める。
Simplification factとしてのイベントを保存するだけ。
O-R間のインピーダンスミスマッチが起きない。
Audit trail システムで何が起きてその状態なのかの追跡が可能となり、監査ログとし
て使える
Integration with
other
subsystems
疎結合を促す。
また、他のサブシステムへ publishした全てのイベントは eventStoreに保存
されている。
Deriving
additional
business value
from the event
history
将来起こるビジネス要件を予想することは困難だが全てのイベント履歴を
持っていることで対処可能性が高まる。
例:ある時点での状態がどうであったかにこたえられるなど
Production
troubleshooting
本番環境のデータをテスト環境にコピーするなどして状態の再現を簡単に
できるためトラブルシューティングに役立つ
Fixing Errors ESの場合factとしてイベントが保存されているだけなので、コーディングエ
ラーを修正するだけで DB値を手動メンテせずにすむ場合がある
Testing テスト容易性が高まる
Flexibility イベント群があればいかなる構造のデータにも変換可能
参考:CQRS jornery(Introducing Event Sourcing)より
将来起こるビジネス要件を
予想することは困難だが
全てのイベント履歴を持っていることで
対処可能性が高まる
copyright Fringe81 Co.,Ltd.
メリット 要約
Performance ESにおいては永続化はイベントの append-only。
UPDATE処理のパフォーマンス改善が望める。
Simplification factとしてのイベントを保存するだけ。
O-R間のインピーダンスミスマッチが起きない。
Audit trail システムで何が起きてその状態なのかの追跡が可能となり、監査ログとし
て使える
Integration with
other
subsystems
疎結合を促す。
また、他のサブシステムへ publishした全てのイベントは eventStoreに保存
されている。
Deriving
additional
business value
from the event
history
将来起こるビジネス要件を予想することは困難だが全てのイベント履歴を
持っていることで対処可能性が高まる。
例:ある時点での状態がどうであったかにこたえられるなど
Production
troubleshooting
本番環境のデータをテスト環境にコピーするなどして状態の再現を簡単に
できるためトラブルシューティングに役立つ
Fixing Errors ESの場合factとしてイベントが保存されているだけなので、コーディングエ
ラーを修正するだけで DB値を手動メンテせずにすむ場合がある
Testing テスト容易性が高まる
Flexibility イベント群があればいかなる構造のデータにも変換可能
参考:CQRS jornery(Introducing Event Sourcing)より
本番環境のデータを
テスト環境にコピーするなどして
状態の再現を簡単にできるため
トラブルシューティングに役立つ
copyright Fringe81 Co.,Ltd.
ところで
copyright Fringe81 Co.,Ltd.
新しいサービス立ち上げの
チャンスが巡ってきた
CQRSアーキテクチャを導入
※当初はEventSourcingはやるつもりなし
copyright Fringe81 Co.,Ltd.
CQRSは比較的受け入れやすい印象
Eventを伝搬させる方法についても
昨今のmicroserviceの文脈からも
受け入れやすい印象
Event Sourcingはどうでしょう?
copyright Fringe81 Co.,Ltd.
メリットは理解できるものの
少し抵抗を感じていました
それはなぜだろう?
(自分のケースで考えてみた)
copyright Fringe81 Co.,Ltd.
1. 慣れ親しんだやり方を変える難しさ
状態を1レコードで表現し
UPDATEを繰り返すスタイル
イベントを積み上げて
状態を表現するスタイル
新しい概念なので
チームメンバーの巻き込み(説得?)
パフォーマンスへの不安
copyright Fringe81 Co.,Ltd.
新しい概念と思っていたのですが
実は意外と古くからありました
copyright Fringe81 Co.,Ltd.
2.技術的チャレンジが多い?
Scala文脈だとESやるにはAkkaを連想するが
ESもakkaも初チャレンジという場合は
やや技術的チャレンジが多い印象?
 ・Actor
 ・Persistence
 ・Query(含: Akka Stream)
 ・Cluster
※使えるなら使ったほうがいいと思います
copyright Fringe81 Co.,Ltd.
3. 部分採用が難しい?
ESは通常アーキテクチャパターン
として語られる
やるかやらないか
copyright Fringe81 Co.,Ltd.
(自分たちの場合)
ESでも出来るというよりは
ESの方が好ましい/じゃなきゃできない
というものが出てきた
小さくはじめるアプローチ
※akkaは今のところなし
copyright Fringe81 Co.,Ltd.
例えば次のような要件部分に
EventSourcingを採用
※少し一般化して
copyright Fringe81 Co.,Ltd.
ケース1:
ECサイトなどのポイント
・ポイントの獲得
・ポイントの消費
・ポイントのキャンセル
・獲得したポイントには有効期限がある
最新の保持ポイント総数を持つだけでは
厳しい
copyright Fringe81 Co.,Ltd.
ケース2:
操作履歴の表示
ADNWなどの広告配信システムにおいては、
何をしたから効果が良くなったのか
もしくは悪くなったのかを把握したい
「配信リーチ対象を変えた」
「クリエイティブを差し替えた」
「1日上限予算を変更した」
などの操作履歴が追えると
ユーザーのPDCAを促すことができそう
copyright Fringe81 Co.,Ltd.
ケース3:
技術的な制約を乗り越えるために(cloud datastore)
同一レコードにupdateが集中するケースに弱い
解決策の1つとしてappend-onlyなES
copyright Fringe81 Co.,Ltd.
EventSourcingの課題
・検索に弱い
・パフォーマンスの課題
copyright Fringe81 Co.,Ltd.
イベント積み上げでは検索に弱い:
ex) 「保有ポイントが350pt以上の人を検索」を簡単
に行えるようにしたい
CQRSと組み合わせていることで
Qに最適化した形にして対応できる
copyright Fringe81 Co.,Ltd.
イベント数増による再生時間増対応:
snapshot機能
なんらかのタイミングでレコードをまとめあげたポイ
ントを作り、再生する回数を削減する
copyright Fringe81 Co.,Ltd.
バッチプログラムが
ドメイン的に意味のある周期で
(weeklyやmonthlyなど)
データを退避させ
まとめあげたレコードを作る
バッチ
バッチ
バッチ
copyright Fringe81 Co.,Ltd.
もしくは
copyright Fringe81 Co.,Ltd.
参考:
 Akka Persistence
※DBはPostgresqlにして
保存状態を見やすくしてみる
with akka-persistence-jdbc
copyright Fringe81 Co.,Ltd.
class ExamplePersistentActor(id: String) extends PersistentActor {
override def persistenceId = id
var state = ExampleState()
def updateState(event: Evt): Unit = {
state = state.updated(event)
}
override val receiveRecover: Receive = {
case evt: Evt => updateState(evt)
case SnapshotOffer(_, snapshot: ExampleState) => state = snapshot
}
override val receiveCommand: Receive = {
case Cmd(data) =>
persist(Evt(data)) { event =>
updateState(event)
if (lastSequenceNr % 10 == 0 && lastSequenceNr != 0) {
saveSnapshot(state.compress)
}
}
}
copyright Fringe81 Co.,Ltd.
class ExamplePersistentActor(id: String) extends PersistentActor {
override def persistenceId = id
var state = ExampleState()
def updateState(event: Evt): Unit = {
state = state.updated(event)
}
override val receiveRecover: Receive = {
case evt: Evt => updateState(evt)
case SnapshotOffer(_, snapshot: ExampleState) => state = snapshot
}
override val receiveCommand: Receive = {
case Cmd(data) =>
persist(Evt(data)) { event =>
updateState(event)
if (lastSequenceNr % 10 == 0 && lastSequenceNr != 0) {
saveSnapshot(state.compress)
}
}
}
10回毎にsnapshot
copyright Fringe81 Co.,Ltd.
copyright Fringe81 Co.,Ltd.
journal
snapshot
copyright Fringe81 Co.,Ltd.
さらにLadderをのぼる
copyright Fringe81 Co.,Ltd.
再生頻度自体の削減
リクエストのたびにEvent群を
毎回再生して状態を復元するのでは
なくてメモリ上に常駐しておくスタイル
ex. akka-cluster sharding
copyright Fringe81 Co.,Ltd.
EventSourcingまとめ
・Eventの永続化
・検索に強く with CQRS
・再生時間の短縮(snapshot)
・再生頻度の削減(onMemory)
copyright Fringe81 Co.,Ltd.
さいごに
CQRS+ESの実装をするための
Fun.CQRSというフレームワーク
実装参考としてして紹介します
http://www.funcqrs.io/
copyright Fringe81 Co.,Ltd.
Akka実装とInMemory実装がある
(もちろん追加可能)
(State[Aggregate], Command) => F[Events]
(State[Aggregate], Event) => State[Aggregate]
WhatとHowの分離
copyright Fringe81 Co.,Ltd.
例)
Lottery(くじ引き)
※このサンプルコードでは
参加者のうち
勝者は1人だけとなる
(当たりは1つ)
copyright Fringe81 Co.,Ltd.
EmptyLottery
NonEmptyLottery
FinishedLottery
CreateLottery
AddParticipant
AddParticipant
RemoveParticipant Run
copyright Fringe81 Co.,Ltd.
LotteryCreated
ParticipantAdded
ParticipantRemoved
WinnerSelected
EmptyLottery
NonEmptyLottery
FinishedLottery
copyright Fringe81 Co.,Ltd.
sealed trait Lottery {
def id: LotteryId
}
case class EmptyLottery(id: LotteryId) extends Lottery
case class NonEmptyLottery(
participants: List[String], id: LotteryId) extends Lottery
case class FinishedLottery(
winner: String, id: LotteryId) extends Lottery
<C側のモデル>
copyright Fringe81 Co.,Ltd.
case class LotteryView(
participants: List[String] = List(),
winner: Option[String] = None,
runDate: Option[OffsetDateTime] = None,
id: LotteryId
)
<Q側のモデル>
copyright Fringe81 Co.,Ltd.
sealed trait LotteryCommand
case object CreateLottery extends LotteryCommand
case class AddParticipant(name:String) extends LotteryCommand
case class RemoveParticipant(name:String) extends LotteryCommand
case object Run extends LotteryCommand
sealed trait LotteryEvent {
def lotteryId: LotteryId
}
case class LotteryCreated(lotteryId: LotteryId) extends LotteryEvent
sealed trait LotteryUpdateEvent extends LotteryEvent
case class ParticipantAdded(name: String, raffleId: LotteryId) extends LotteryUpdateEvent
case class ParticipantRemoved(name: String, raffleId: LotteryId) extends LotteryUpdateEvent
case class WinnerSelected(winner:String,date:OffsetDateTime,lotteryId:LotteryId) extends LotteryUpdateEvent
<CommandとEvent>
copyright Fringe81 Co.,Ltd.
(State[Lottery], LotteryCommand) => LotteryEvents
(State[Lottery], LotteryEvent) => State[Lottery]
つまり
copyright Fringe81 Co.,Ltd.
C側
copyright Fringe81 Co.,Ltd.
case class NonEmptyLottery(...) extends Lottery {
def acceptParticipants = ...
def removeParticipants = …
def runTheLottery =
Lottery.actions
.commandHandler {
OneEvent {
case Run =>
val winner = participants(Random.nextInt(participants.size))
WinnerSelected(winner, OffsetDateTime.now, id)
}
}
.eventHandler {
case evt: WinnerSelected => FinishedLottery(evt.winner, id)
}
Command => Event
copyright Fringe81 Co.,Ltd.
case class NonEmptyLottery(...) extends Lottery {
def acceptParticipants = ...
def removeParticipants = …
def runTheLottery =
Lottery.actions
.commandHandler {
OneEvent {
case Run =>
val winner = participants(Random.nextInt(participants.size))
WinnerSelected(winner, OffsetDateTime.now, id)
}
}
.eventHandler {
case evt: WinnerSelected => FinishedLottery(evt.winner, id)
}
Event => Aggregate
copyright Fringe81 Co.,Ltd.
object Lottery extends Types[Lottery] {
type Id = LotteryId
type Command = LotteryCommand
type Event = LotteryEvent
def create(lotteryId: LotteryId) = ...
def behavior(lotteryId: LotteryId): Behavior[Lottery, LotteryCommand, LotteryEvent] =
Behavior
.first {
create(lotteryId)
}
.andThen {
case r: EmptyLottery =>
r.acceptParticipants
case r: NonEmptyLottery =>
r.acceptParticipants ++ r.removeParticipants ++ r.runTheLottery
case r: FinishedLottery =>
r.rejectAllCommands
}
copyright Fringe81 Co.,Ltd.
Q側
EventをもとにQへ写す
(Projectionする)
copyright Fringe81 Co.,Ltd.
class LotteryViewProjection(dao: LotteryDAO) extends Projection[LotteryEvent] {
def handleEvent = attempt.HandleEvent {
case evt: LotteryCreated =>
dao.save(LotteryView(id = evt.raffleId))
case evt: LotteryUpdateEvent =>
dao.updateById(evt.lotteryId) { r =>
evt match {
case e: ParticipantAdded =>
r.copy(participants = r.participants :+ newParticipant(e))
case e: ParticipantRemoved =>
r.copy(participants = r.participants.filter(_.name != e.name))
case e: WinnerSelected =>
r.copy(winner = Some(e.winner), runDate = Some(e.date))
}
}
}
Eventをもとに
Q側DBを更新
copyright Fringe81 Co.,Ltd.
最後に定義
copyright Fringe81 Co.,Ltd.
object AppContext {
val backend = {
val inMemoryBackend = new InMemoryBackend
inMemoryBackend
.configure { // “C” in CQRS
aggregate(Lottery.behavior)
}
.configure { // “Q” in CQRS
projection(
projection = new LotteryViewProjection(new LotteryDAO()),
publisherFactory =
inMemoryBackend.inMemoryPublisherFactory[LotteryEvent]
)
}
}
}
copyright Fringe81 Co.,Ltd.
object AppContext {
val backend = {
val inMemoryBackend = new InMemoryBackend
inMemoryBackend
.configure { // “C” in CQRS
aggregate(Lottery.behavior)
}
.configure { // “Q” in CQRS
projection(
projection = new LotteryViewProjection(new LotteryDAO()),
publisherFactory =
inMemoryBackend.inMemoryPublisherFactory[LotteryEvent]
)
}
}
}
copyright Fringe81 Co.,Ltd.
実行
copyright Fringe81 Co.,Ltd.
val lotteryRef =
AppContext.backend.aggregateRef[Lottery].forId(lotteryId)
lotteryRef ! CreateLottery
lotteryRef ! AddParticipant("Ichiro")
lotteryRef ! AddParticipant("Jiro")
lotteryRef ! AddParticipant("Saburo")
lotteryRef ! Run
dao.find(lotteryId) match {
case Success(r) => println(r.winner)
case Failure(t) => println(t.getMessage)
}
Command
を送る
copyright Fringe81 Co.,Ltd.
val lotteryRef =
AppContext.backend.aggregateRef[Lottery].forId(lotteryId)
lotteryRef ! CreateLottery
lotteryRef ! AddParticipant("Ichiro")
lotteryRef ! AddParticipant("Jiro")
lotteryRef ! AddParticipant("Saburo")
lotteryRef ! Run
dao.find(lotteryId) match {
case Success(r) => println(r.winner)
case Failure(t) => println(t.getMessage)
}
Q側を
確認
copyright Fringe81 Co.,Ltd.
CQRS
EventSourcing
ありがとうございました

More Related Content

What's hot

Swagger jjug ccc 2018 spring
Swagger jjug ccc 2018 springSwagger jjug ccc 2018 spring
Swagger jjug ccc 2018 spring
kounan13
 

What's hot (20)

Spring I/O 2017 報告 ThymeleafのWebFlux対応
Spring I/O 2017 報告 ThymeleafのWebFlux対応Spring I/O 2017 報告 ThymeleafのWebFlux対応
Spring I/O 2017 報告 ThymeleafのWebFlux対応
 
Swagger jjug ccc 2018 spring
Swagger jjug ccc 2018 springSwagger jjug ccc 2018 spring
Swagger jjug ccc 2018 spring
 
Building andobservingcloudnativeappliactionusingazure elastic-terraform
Building andobservingcloudnativeappliactionusingazure elastic-terraformBuilding andobservingcloudnativeappliactionusingazure elastic-terraform
Building andobservingcloudnativeappliactionusingazure elastic-terraform
 
Fantiaから学ぶgcp運用のノウハウ
Fantiaから学ぶgcp運用のノウハウFantiaから学ぶgcp運用のノウハウ
Fantiaから学ぶgcp運用のノウハウ
 
Keynote
KeynoteKeynote
Keynote
 
【20211027_toranoana.deno#2】とりあえずDenoを CloudRunで動かしてみる
【20211027_toranoana.deno#2】とりあえずDenoを CloudRunで動かしてみる【20211027_toranoana.deno#2】とりあえずDenoを CloudRunで動かしてみる
【20211027_toranoana.deno#2】とりあえずDenoを CloudRunで動かしてみる
 
20190604 Containerized MagicOnion on kubernetes with Observability with New R...
20190604 Containerized MagicOnion on kubernetes with Observability with New R...20190604 Containerized MagicOnion on kubernetes with Observability with New R...
20190604 Containerized MagicOnion on kubernetes with Observability with New R...
 
決済システムの内製化への旅 - SpringとPCFで作るクラウドネイティブなシステム開発 #jsug #sf_h1
決済システムの内製化への旅 - SpringとPCFで作るクラウドネイティブなシステム開発 #jsug #sf_h1決済システムの内製化への旅 - SpringとPCFで作るクラウドネイティブなシステム開発 #jsug #sf_h1
決済システムの内製化への旅 - SpringとPCFで作るクラウドネイティブなシステム開発 #jsug #sf_h1
 
Google Cloud Platform で実現するプロダクションレディ マイクロサービス
Google Cloud Platform で実現するプロダクションレディ マイクロサービスGoogle Cloud Platform で実現するプロダクションレディ マイクロサービス
Google Cloud Platform で実現するプロダクションレディ マイクロサービス
 
[Cloud OnAir] GCP で構築するセキュアなサービス。基本と最新プロダクトのご紹介 2018年11月1日 放送
[Cloud OnAir] GCP で構築するセキュアなサービス。基本と最新プロダクトのご紹介 2018年11月1日 放送[Cloud OnAir] GCP で構築するセキュアなサービス。基本と最新プロダクトのご紹介 2018年11月1日 放送
[Cloud OnAir] GCP で構築するセキュアなサービス。基本と最新プロダクトのご紹介 2018年11月1日 放送
 
Sumo Logic活用事例とその運用
Sumo Logic活用事例とその運用Sumo Logic活用事例とその運用
Sumo Logic活用事例とその運用
 
システム高速化フォーラム向け プッシュ通知基盤のアーキテクチャ
システム高速化フォーラム向け プッシュ通知基盤のアーキテクチャシステム高速化フォーラム向け プッシュ通知基盤のアーキテクチャ
システム高速化フォーラム向け プッシュ通知基盤のアーキテクチャ
 
Spanner から GKE、Spinnaker、そして SRE まで、コロプラが今挑戦していること[Google Cloud INSIDE Games ...
Spanner から GKE、Spinnaker、そして SRE まで、コロプラが今挑戦していること[Google Cloud INSIDE Games ...Spanner から GKE、Spinnaker、そして SRE まで、コロプラが今挑戦していること[Google Cloud INSIDE Games ...
Spanner から GKE、Spinnaker、そして SRE まで、コロプラが今挑戦していること[Google Cloud INSIDE Games ...
 
GAE + Spannerで目指せ No (Uncomfortable) Ops
GAE + Spannerで目指せ No (Uncomfortable) OpsGAE + Spannerで目指せ No (Uncomfortable) Ops
GAE + Spannerで目指せ No (Uncomfortable) Ops
 
Microsoft open tech night 2020 feb18
Microsoft open tech night 2020 feb18Microsoft open tech night 2020 feb18
Microsoft open tech night 2020 feb18
 
Spring tools4
Spring tools4Spring tools4
Spring tools4
 
Dist 29 gcp_serverless_web_app_development
Dist 29 gcp_serverless_web_app_developmentDist 29 gcp_serverless_web_app_development
Dist 29 gcp_serverless_web_app_development
 
Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26
Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26
Micrometer/Prometheusによる大規模システムモニタリング #jsug #sf_26
 
今まで公開してきた Deno Third Party Modules -- Deno での WebAssembly の利用の話 --
今まで公開してきた Deno Third Party Modules -- Deno での WebAssembly の利用の話 --今まで公開してきた Deno Third Party Modules -- Deno での WebAssembly の利用の話 --
今まで公開してきた Deno Third Party Modules -- Deno での WebAssembly の利用の話 --
 
Spring I/O 2015 報告
Spring I/O 2015 報告Spring I/O 2015 報告
Spring I/O 2015 報告
 

Viewers also liked

Viewers also liked (8)

受託開発をやりながらboardを軌道に乗せるまで
受託開発をやりながらboardを軌道に乗せるまで受託開発をやりながらboardを軌道に乗せるまで
受託開発をやりながらboardを軌道に乗せるまで
 
「関心事」と「責務」 の お話
「関心事」と「責務」 の お話「関心事」と「責務」 の お話
「関心事」と「責務」 の お話
 
受託の会社が調達せずに自社サービスを立ち上げ事業として成立するまでの企画・開発・サポート・マーケティング
受託の会社が調達せずに自社サービスを立ち上げ事業として成立するまでの企画・開発・サポート・マーケティング受託の会社が調達せずに自社サービスを立ち上げ事業として成立するまでの企画・開発・サポート・マーケティング
受託の会社が調達せずに自社サービスを立ち上げ事業として成立するまでの企画・開発・サポート・マーケティング
 
ITエンジニアのための英語勉強法
ITエンジニアのための英語勉強法ITエンジニアのための英語勉強法
ITエンジニアのための英語勉強法
 
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3 データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
 
Java を今すぐダウンロードしてみたお話
Java を今すぐダウンロードしてみたお話Java を今すぐダウンロードしてみたお話
Java を今すぐダウンロードしてみたお話
 
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_cccJEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
 

Similar to Ladder of cqrs+es

IoTアプリ/ロボット開発をリアルタイムOSでレベルアップしませんか? ~高品質な組込み向けオープンソースを開発するTOPPERSプロジェクトのご紹介~
IoTアプリ/ロボット開発をリアルタイムOSでレベルアップしませんか? ~高品質な組込み向けオープンソースを開発するTOPPERSプロジェクトのご紹介~IoTアプリ/ロボット開発をリアルタイムOSでレベルアップしませんか? ~高品質な組込み向けオープンソースを開発するTOPPERSプロジェクトのご紹介~
IoTアプリ/ロボット開発をリアルタイムOSでレベルアップしませんか? ~高品質な組込み向けオープンソースを開発するTOPPERSプロジェクトのご紹介~
Hideki Takase
 
ライブラリ作成のすゝめ - 事例から見る個人OSS開発の効能
ライブラリ作成のすゝめ - 事例から見る個人OSS開発の効能ライブラリ作成のすゝめ - 事例から見る個人OSS開発の効能
ライブラリ作成のすゝめ - 事例から見る個人OSS開発の効能
Yoshifumi Kawai
 

Similar to Ladder of cqrs+es (20)

Sparkコミュニティに飛び込もう!(Spark Meetup Tokyo 2015 講演資料、NTTデータ 猿田 浩輔)
Sparkコミュニティに飛び込もう!(Spark Meetup Tokyo 2015 講演資料、NTTデータ 猿田 浩輔)Sparkコミュニティに飛び込もう!(Spark Meetup Tokyo 2015 講演資料、NTTデータ 猿田 浩輔)
Sparkコミュニティに飛び込もう!(Spark Meetup Tokyo 2015 講演資料、NTTデータ 猿田 浩輔)
 
Amazon EKSによるスケーラブルなCTR予測システム
Amazon EKSによるスケーラブルなCTR予測システムAmazon EKSによるスケーラブルなCTR予測システム
Amazon EKSによるスケーラブルなCTR予測システム
 
Tensor flow勉強会3
Tensor flow勉強会3Tensor flow勉強会3
Tensor flow勉強会3
 
小規模チームで Type script と向き合う話
小規模チームで Type script と向き合う話小規模チームで Type script と向き合う話
小規模チームで Type script と向き合う話
 
おすすめインフラ! for スタートアップ
おすすめインフラ! for スタートアップおすすめインフラ! for スタートアップ
おすすめインフラ! for スタートアップ
 
リクルート式 自然言語処理技術の適応事例紹介
リクルート式 自然言語処理技術の適応事例紹介リクルート式 自然言語処理技術の適応事例紹介
リクルート式 自然言語処理技術の適応事例紹介
 
Airflowを広告データのワークフローエンジンとして運用してみた話
Airflowを広告データのワークフローエンジンとして運用してみた話Airflowを広告データのワークフローエンジンとして運用してみた話
Airflowを広告データのワークフローエンジンとして運用してみた話
 
OSC 2020 Fukuoka IT運用自動化を支援する「運用レコメンドプラットフォーム」実現の舞台裏
OSC 2020 Fukuoka IT運用自動化を支援する「運用レコメンドプラットフォーム」実現の舞台裏OSC 2020 Fukuoka IT運用自動化を支援する「運用レコメンドプラットフォーム」実現の舞台裏
OSC 2020 Fukuoka IT運用自動化を支援する「運用レコメンドプラットフォーム」実現の舞台裏
 
IoTアプリ/ロボット開発をリアルタイムOSでレベルアップしませんか? ~高品質な組込み向けオープンソースを開発するTOPPERSプロジェクトのご紹介~
IoTアプリ/ロボット開発をリアルタイムOSでレベルアップしませんか? ~高品質な組込み向けオープンソースを開発するTOPPERSプロジェクトのご紹介~IoTアプリ/ロボット開発をリアルタイムOSでレベルアップしませんか? ~高品質な組込み向けオープンソースを開発するTOPPERSプロジェクトのご紹介~
IoTアプリ/ロボット開発をリアルタイムOSでレベルアップしませんか? ~高品質な組込み向けオープンソースを開発するTOPPERSプロジェクトのご紹介~
 
.NETの自作ツール公開手段
.NETの自作ツール公開手段.NETの自作ツール公開手段
.NETの自作ツール公開手段
 
インフラCICDの勘所
インフラCICDの勘所インフラCICDの勘所
インフラCICDの勘所
 
ライブラリ作成のすゝめ - 事例から見る個人OSS開発の効能
ライブラリ作成のすゝめ - 事例から見る個人OSS開発の効能ライブラリ作成のすゝめ - 事例から見る個人OSS開発の効能
ライブラリ作成のすゝめ - 事例から見る個人OSS開発の効能
 
OpenStack Swiftとそのエコシステムの最新動向
OpenStack Swiftとそのエコシステムの最新動向OpenStack Swiftとそのエコシステムの最新動向
OpenStack Swiftとそのエコシステムの最新動向
 
Kubernetesのしくみ やさしく学ぶ 内部構造とアーキテクチャー
Kubernetesのしくみ やさしく学ぶ 内部構造とアーキテクチャーKubernetesのしくみ やさしく学ぶ 内部構造とアーキテクチャー
Kubernetesのしくみ やさしく学ぶ 内部構造とアーキテクチャー
 
AlexaにSkillを追加してチョメチョメしてみた
AlexaにSkillを追加してチョメチョメしてみたAlexaにSkillを追加してチョメチョメしてみた
AlexaにSkillを追加してチョメチョメしてみた
 
GoAzure 2015:IoTなどの大量データをStream Analyticsでリアルタイムデータ分析してみよう
GoAzure 2015:IoTなどの大量データをStream Analyticsでリアルタイムデータ分析してみようGoAzure 2015:IoTなどの大量データをStream Analyticsでリアルタイムデータ分析してみよう
GoAzure 2015:IoTなどの大量データをStream Analyticsでリアルタイムデータ分析してみよう
 
AWSスポットインスタンスの真髄
AWSスポットインスタンスの真髄AWSスポットインスタンスの真髄
AWSスポットインスタンスの真髄
 
Dangerでpull requestレビューの指摘事項を減らす
Dangerでpull requestレビューの指摘事項を減らすDangerでpull requestレビューの指摘事項を減らす
Dangerでpull requestレビューの指摘事項を減らす
 
Automation with SoftLayer and Zabbix
Automation with SoftLayer and ZabbixAutomation with SoftLayer and Zabbix
Automation with SoftLayer and Zabbix
 
OpenStack Day Tokyo 2013 - Morphlabs - Satoshi Konno
OpenStack Day Tokyo 2013 - Morphlabs - Satoshi KonnoOpenStack Day Tokyo 2013 - Morphlabs - Satoshi Konno
OpenStack Day Tokyo 2013 - Morphlabs - Satoshi Konno
 

Ladder of cqrs+es