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
LLM Çağında Backend Olmak: 10 Milyon Prompt'u Milisaniyede Sorgulamak
selcukusta
0
140
PC-6001でPSG曲を鳴らすまでを全部NetBSD上の Makefile に押し込んでみた / osc2025hiroshima
tsutsui
0
200
【卒業研究】会話ログ分析によるユーザーごとの関心に応じた話題提案手法
momok47
0
160
副作用をどこに置くか問題:オブジェクト指向で整理する設計判断ツリー
koxya
1
240
Spinner 軸ズレ現象を調べたらレンダリング深淵に飲まれた #レバテックMeetup
bengo4com
1
210
안드로이드 9년차 개발자, 프론트엔드 주니어로 커리어 리셋하기
maryang
1
150
ゆくKotlin くるRust
exoego
1
190
C-Shared Buildで突破するAI Agent バックテストの壁
po3rin
0
430
AIで開発はどれくらい加速したのか?AIエージェントによるコード生成を、現場の評価と研究開発の評価の両面からdeep diveしてみる
daisuketakeda
1
390
Basic Architectures
denyspoltorak
0
160
AI Agent Dojo #4: watsonx Orchestrate ADK体験
oniak3ibm
PRO
0
120
脳の「省エネモード」をデバッグする ~System 1(直感)と System 2(論理)の切り替え~
panda728
PRO
0
130
Featured
See All Featured
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
7.9k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.4k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
260
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.6k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.3k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
37
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
230
Leading Effective Engineering Teams in the AI Era
addyosmani
9
1.4k
The Invisible Side of Design
smashingmag
302
51k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
74
Designing Experiences People Love
moore
143
24k
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 ಡॻձ ڵຯͷ͋Δํ͓͕͚͍ͩ͘͞ɻ
͋Γ͕ͱ͏͍͟͝·ͨ͠