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
1
110
CQRS+EventSourcingをAkka Persistenceを使って実装してみる
CQRS+EventSourcingをAkka Persistenceを使って実装してみる
satoshi-m8a
March 16, 2016
Tweet
Share
More Decks by satoshi-m8a
See All by satoshi-m8a
Akka Persistent FSM を使ってみる。
satoshim8a
0
120
Other Decks in Programming
See All in Programming
20260127_試行錯誤の結晶を1冊に。著者が解説 先輩データサイエンティストからの指南書 / author's_commentary_ds_instructions_guide
nash_efp
1
1k
Gemini for developers
meteatamel
0
100
AIによるイベントストーミング図からのコード生成 / AI-powered code generation from Event Storming diagrams
nrslib
2
1.9k
MDN Web Docs に日本語翻訳でコントリビュート
ohmori_yusuke
0
660
Raku Raku Notion 20260128
hareyakayuruyaka
0
370
ノイジーネイバー問題を解決する 公平なキューイング
occhi
0
110
Vibe Coding - AI 驅動的軟體開發
mickyp100
0
180
React Native × React Router v7 API通信の共通化で考えるべきこと
suguruooki
0
100
KIKI_MBSD Cybersecurity Challenges 2025
ikema
0
1.3k
CSC307 Lecture 09
javiergs
PRO
1
840
humanlayerのブログから学ぶ、良いCLAUDE.mdの書き方
tsukamoto1783
0
200
なぜSQLはAIぽく見えるのか/why does SQL look AI like
florets1
0
480
Featured
See All Featured
WENDY [Excerpt]
tessaabrams
9
36k
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
280
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.6k
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
590
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
0
160
From π to Pie charts
rasagy
0
130
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
9.9k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.1k
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
310
Google's AI Overviews - The New Search
badams
0
910
Deep Space Network (abreviated)
tonyrice
0
66
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 ಡॻձ ڵຯͷ͋Δํ͓͕͚͍ͩ͘͞ɻ
͋Γ͕ͱ͏͍͟͝·ͨ͠