Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
CQRS+EventSourcingをAkka Persistenceを使って実装してみる
Search
satoshi-m8a
March 16, 2016
Programming
120
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
CQRS+EventSourcingをAkka Persistenceを使って実装してみる
CQRS+EventSourcingをAkka Persistenceを使って実装してみる
satoshi-m8a
March 16, 2016
More Decks by satoshi-m8a
See All by satoshi-m8a
Akka Persistent FSM を使ってみる。
satoshim8a
0
130
Other Decks in Programming
See All in Programming
変わらないものが、変わるものを決める — 意図駆動開発 × イベントソーシング × イミュータブル | What Doesn't Change Decides What Can — IDD × Event Sourcing × Immutability
tomohisa
0
1.5k
【やさしく解説 設計編・中級 #4】ルールの寿命と、システムの年輪
panda728
PRO
2
180
自動化したのに回らないテスト運用の壁ーAI時代の品質責任と生産性
mfunaki
0
130
OpenSpecのproposalにbrainstormingを持たせてみた
tigertora7571
1
220
Cloudflare is Agents
chimame
0
160
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
560
言葉の格闘技のススメ~紙とペンと言葉から始める、キャリアの描き方~
progresscicada
2
140
freee が目指す データ マネジメント戦略 AI-Ready 時代を支える 攻めのガバナンスとは
freee
PRO
0
350
TSX の <Hoge<Fuga>> という構文に驚いた話 / tsx-type-argument-syntax
kanaru0928
0
220
PHP Application における Kubernetes 内 gRPC 通信
ganchiku
0
590
AIが無かった頃の素敵な出会いの話
codmoninc
1
420
FDEが実現するAI駆動経営の現在地
gonta
2
280
Featured
See All Featured
Designing for Timeless Needs
cassininazir
1
440
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
340
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Scaling GitHub
holman
464
140k
Balancing Empowerment & Direction
lara
6
1.2k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
410
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
280
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
470
Tell your own story through comics
letsgokoyo
1
1k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
270
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
480
Transcript
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 Ϣʔβʔ ϑΥϩΠʔͷϦετ ϒϩοΫ͞Ε͍ͯΔ ϢʔβʔͷϦετ ίϚϯυαΠυ ΫΤϦαΠυ ϑΥϩϫʔͷϦετ ϑΥϩΠʔͷϦετ ϒϩοΫ͞Ε͍ͯΔ
ϢʔβʔͷϦετ ϒϩοΫ͍ͯ͠Δ ϢʔβʔͷϦετ … ϢʔβʔͷϦετ
ෳࡶ͞ʹཱ͔ͪ͏ • ෳࡶͳυϝΠϯΛɺͦͷ··ෳࡶͳυϝΠϯϞσϧ ʹམͱͯ͠ຬ͕ͪ͠ • ·ͣɺίϯςΩετׂΛݕ౼ • υϝΠϯΛΑ͘؍͠ɺৼΔ͍ʹϑΥʔΧε͢Δ • CQRSESͷݕ౼ͦͷ͋ͱ
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 APIDDDʹ͔ͳ͍ • 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 PluginAkkaެࣜ
ΫΤϦαΠυ
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 ಡॻձ ڵຯͷ͋Δํ͓͕͚͍ͩ͘͞ɻ
͋Γ͕ͱ͏͍͟͝·ͨ͠