akka persistence == event sourcing in 30 minutes
DESCRIPTION
Akka 2.3 introduces akka-persistence, a wonderful way of implementing event-sourced applications. Let's give it a shot and see how DDD and Akka are a match made in heaven :-)TRANSCRIPT
![Page 1: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/1.jpg)
Akka persistence (message sourcing in 30 minutes)
Konrad 'ktoso' Malawski Scalar 2014 @ Warsaw, PL
![Page 2: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/2.jpg)
Konrad `@ktosopl` Malawski
typesafe.com geecon.org
Java.pl / KrakowScala.pl sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl meetup.com/Lambda-Lounge-Krakow
![Page 3: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/3.jpg)
Konrad `@ktosopl` Malawski
typesafe.com geecon.org
Java.pl / KrakowScala.pl sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl meetup.com/Lambda-Lounge-Krakow
![Page 4: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/4.jpg)
Konrad `@ktosopl` Malawski
typesafe.com geecon.org
Java.pl / KrakowScala.pl sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl meetup.com/Lambda-Lounge-Krakow
![Page 5: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/5.jpg)
Konrad `@ktosopl` Malawski
typesafe.com geecon.org
Java.pl / KrakowScala.pl sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl meetup.com/Lambda-Lounge-Krakow
![Page 6: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/6.jpg)
Konrad `@ktosopl` Malawski
typesafe.com geecon.org
Java.pl / KrakowScala.pl sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl meetup.com/Lambda-Lounge-Krakow
![Page 7: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/7.jpg)
Konrad `@ktosopl` Malawski
typesafe.com geecon.org
Java.pl / KrakowScala.pl sckrk.com / meetup.com/Paper-Cup @ London
GDGKrakow.pl meetup.com/Lambda-Lounge-Krakow
hAkker @
![Page 8: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/8.jpg)
mainly by Martin Krasser !!(as contractor for Typesafe) !inspired by:
akka-persistence
https://github.com/krasserm
https://github.com/eligosource/eventsourced
![Page 9: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/9.jpg)
dependencies
libraryDependencies ++= Seq(! "com.typesafe.akka" %% “akka-actor" % "2.3.0",! "com.typesafe.akka" %% "akka-persistence-experimental" % "2.3.0"!)
![Page 10: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/10.jpg)
Show of hands!
![Page 11: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/11.jpg)
Show of hands!
![Page 12: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/12.jpg)
Show of hands!
![Page 13: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/13.jpg)
Show of hands!
![Page 14: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/14.jpg)
sourcing styles
Command Sourcing Event Sourcing
msg: DoThing
msg persisted before receive
imperative, “do the thing”
business logic change, can be reflected in reaction
Processor
![Page 15: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/15.jpg)
sourcing styles
Command Sourcing Event Sourcing
msg: DoThing msg: ThingDone
msg persisted before receive commands converted to events, must be manually persisted
imperative, “do the thing” past tense, “happened”
business logic change, can be reflected in reaction
business logic change, won’t change previous events
Processor EventsourcedProcessor
![Page 16: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/16.jpg)
Plain Actors
![Page 17: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/17.jpg)
count: 0 !
!
Actor
![Page 18: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/18.jpg)
count: 0 !
!
ActorAn Actor that keeps count of messages it processed
![Page 19: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/19.jpg)
count: 0 !
!
ActorAn Actor that keeps count of messages it processed
![Page 20: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/20.jpg)
count: 0 !
!
ActorAn Actor that keeps count of messages it processed
Let’s send 2 messages to it
![Page 21: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/21.jpg)
count: 0 !
!
ActorAn Actor that keeps count of messages it processed
Let’s send 2 messages to it(it’s “commands”)
![Page 22: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/22.jpg)
Actor
!!class Counter extends Actor {! var count = 0! def receive = {! case _ => count += 1! }!}
![Page 23: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/23.jpg)
count: 0 !
!
Actor
![Page 24: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/24.jpg)
count: 0 !
!
Actor
![Page 25: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/25.jpg)
count: 1 !
!
Actor
![Page 26: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/26.jpg)
count: 1 !
!
Actor
crash!
![Page 27: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/27.jpg)
Actor
crash!
![Page 28: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/28.jpg)
Actor
restart
![Page 29: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/29.jpg)
count: 0 !
!
Actor
restart
![Page 30: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/30.jpg)
count: 0 !
!
Actor
restarted
![Page 31: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/31.jpg)
count: 1 !
!
Actor
restarted
![Page 32: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/32.jpg)
count: 1 !
!
Actor
restarted
![Page 33: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/33.jpg)
count: 1 !
!wrong!
expected count == 2!
Actor
restarted
![Page 34: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/34.jpg)
Processor
![Page 35: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/35.jpg)
var count = 0 !
def processorId = “a” !
Journal (DB)
!
!
!
Processor
![Page 36: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/36.jpg)
Journal (DB)
!
!
!
Processor
var count = 0 !
def processorId = “a” !
![Page 37: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/37.jpg)
Journal (DB)
!
!
!
Processor
var count = 0 !
def processorId = “a” !
![Page 38: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/38.jpg)
Journal (DB)
!
!
!
Processor
var count = 0 !
def processorId = “a” !
![Page 39: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/39.jpg)
Journal (DB)
!
!
!
Processor
var count = 1 !
def processorId = “a” !
![Page 40: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/40.jpg)
Journal (DB)
!
!
!
Processor
var count = 1 !
def processorId = “a” !
![Page 41: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/41.jpg)
Journal (DB)
!
!
!
Processor
var count = 1 !
def processorId = “a” !
crash!
![Page 42: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/42.jpg)
Journal (DB)
!
!
!
Processor
![Page 43: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/43.jpg)
Journal (DB)
!
!
!
Processor
restart
![Page 44: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/44.jpg)
Journal (DB)
!
!
!
Processor
var count = 0 !
def processorId = “a” !
restart
![Page 45: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/45.jpg)
Journal (DB)
!
!
!
Processor
var count = 0 !
def processorId = “a” !
replay!
restart
![Page 46: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/46.jpg)
Journal (DB)
!
!
!
Processor
var count = 0 !
def processorId = “a” !
![Page 47: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/47.jpg)
Journal (DB)
!
!
!
Processor
var count = 0 !
def processorId = “a” !
replay!
![Page 48: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/48.jpg)
Journal (DB)
!
!
!
Processor
var count = 1 !
def processorId = “a” !
![Page 49: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/49.jpg)
Journal (DB)
!
!
!
Processor
var count = 1 !
def processorId = “a” !
replay!
![Page 50: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/50.jpg)
Journal (DB)
!
!
!
Processor
var count = 1 !
def processorId = “a” !
![Page 51: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/51.jpg)
Journal (DB)
!
!
!
Processor
var count = 2 !
def processorId = “a” !
![Page 52: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/52.jpg)
Journal (DB)
!
!
!
Processor
var count = 2 !
def processorId = “a” !
yay!
![Page 53: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/53.jpg)
Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1! }!}
![Page 54: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/54.jpg)
Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1! }!}
counter ! Persistent(payload)
![Page 55: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/55.jpg)
Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1! }!}
counter ! Persistent(payload)
![Page 56: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/56.jpg)
Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1! }!}
counter ! Persistent(payload)
![Page 57: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/57.jpg)
Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1! }!}
counter ! Persistent(payload)
is already persisted!
![Page 58: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/58.jpg)
Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1! }!}
counter ! Persistent(payload)
sequenceNr (generated by akka)
is already persisted!
![Page 59: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/59.jpg)
Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case notPersisted =>! // will not replay this msg!! count += 1! }!}
counter ! payload
![Page 60: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/60.jpg)
Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case notPersisted =>! // will not replay this msg!! count += 1! }!}
counter ! payload
won’t persist
![Page 61: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/61.jpg)
Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case notPersisted =>! // will not replay this msg!! count += 1! }!}
counter ! payload
won’t persist
won’t replay
![Page 62: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/62.jpg)
Processorimport akka.persistence._!!class CounterProcessor extends Processor {! var count = 0! override val processorId = "counter"!! def receive = {! case Persistent(payload, seqNr) =>! // payload already persisted! count += 1!! case notPersistentMsg =>! // msg not persisted - like in normal Actor! count += 1! }!}
![Page 63: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/63.jpg)
ProcessorUpsides
• Persistent Command Sourcing “out of the box”
• Pretty simple, persist handled for you
• Once you get the msg, it’s persisted
• Pluggable Journals (HBase, Cassandra, Mongo, …)
• Can replay to given seqNr (post-mortem etc!)
![Page 64: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/64.jpg)
ProcessorDownsides
• Exposes Persistent() to Actors who talk to you
• No room for validation before persisting
• There’s one Model, we act on the incoming msg
• Lower throughput than plain Actor (limited by DB)
![Page 65: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/65.jpg)
EventsourcedProcessor
![Page 66: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/66.jpg)
EventsourcedProcessor (longer name == more flexibility);-)
![Page 67: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/67.jpg)
super quick domain modeling!
![Page 68: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/68.jpg)
super quick domain modeling!
sealed trait Command!case class ManyCommand(nums: List[Int]) extends Command
Commands - input from user, “send emails”, not persisted
![Page 69: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/69.jpg)
super quick domain modeling!
sealed trait Command!case class ManyCommand(nums: List[Int]) extends Command
Commands - input from user, “send emails”, not persisted
sealed trait Event!case class AddOneEvent(num: Int) extends Event!
Events - business events emitted by the processor, persisted
![Page 70: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/70.jpg)
super quick domain modeling!
sealed trait Command!case class ManyCommand(nums: List[Int]) extends Command
Commands - input from user, “send emails”, not persisted
case class State(count: Int) {! def updated(more: Int) = State(count + more)!}
State - internal actor state, kept in var
sealed trait Event!case class AddOneEvent(num: Int) extends Event!
Events - business events emitted by the processor, persisted
![Page 71: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/71.jpg)
var count = 0 !
def processorId = “a” !
EventsourcedProcessor
!
!
Journal
C1
![Page 72: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/72.jpg)
var count = 0 !
def processorId = “a” !
EventsourcedProcessor
Command
!
!
Journal
C1
![Page 73: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/73.jpg)
var count = 0 !
def processorId = “a” !
EventsourcedProcessor
!
!
Journal
C1
![Page 74: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/74.jpg)
var count = 0 !
def processorId = “a” !
EventsourcedProcessor
!
!
Journal
C1
Generate Events
![Page 75: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/75.jpg)
C1
var count = 0 !
def processorId = “a” !
EventsourcedProcessor
!
!
Journal
E1
![Page 76: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/76.jpg)
C1
var count = 0 !
def processorId = “a” !
EventsourcedProcessor
!
!
Journal
E1
Event
![Page 77: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/77.jpg)
C1
var count = 0 !
def processorId = “a” !
EventsourcedProcessor
!
!
Journal
E1
![Page 78: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/78.jpg)
C1
var count = 0 !
def processorId = “a” !
EventsourcedProcessor
!
!
Journal
E1
ACK “persisted”
![Page 79: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/79.jpg)
C1
var count = 1 !
def processorId = “a” !
EventsourcedProcessor
!
!
Journal
E1
E1
![Page 80: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/80.jpg)
C1
var count = 1 !
def processorId = “a” !
EventsourcedProcessor
!
!
Journal
E1
E1
“Apply” event
![Page 81: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/81.jpg)
EventsourcedProcessor
class MultiCounter extends EventsourcedProcessor {!! var state = State(count = 0)!! def updateState(e: Event): State = {! case event: AddOneEvent => state.updated(event.num)! }! ! // API:!! def receiveCommand = ??? // TODO!! def receiveRecover = ??? // TODO!!}!
![Page 82: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/82.jpg)
EventsourcedProcessor
def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}
![Page 83: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/83.jpg)
EventsourcedProcessor
def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}
![Page 84: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/84.jpg)
EventsourcedProcessor
def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}
![Page 85: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/85.jpg)
EventsourcedProcessor
def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}
async persist that event
![Page 86: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/86.jpg)
EventsourcedProcessor
def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}
async persist that event
async called after successful persist
![Page 87: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/87.jpg)
EventsourcedProcessor
def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}
async persist that event
async called after successful persist
It is guaranteed that no new commands will be received by a processor between a call to `persist` and the execution of its `handler`.
![Page 88: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/88.jpg)
EventsourcedProcessor
def receiveCommand = {! case ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }!! case command: Command =>! persist(AddOneEvent(command)) { state = updateState(_) }!}
![Page 89: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/89.jpg)
EventsourcedProcessorcase ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }
![Page 90: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/90.jpg)
EventsourcedProcessorcase ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }
“style fix”
![Page 91: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/91.jpg)
EventsourcedProcessorcase ManyCommand(many) =>! for (event <- many map AddOneEvent)! persist(event) { state = updateState(_) }
case ManyCommand(many) =>! persist(many map AddOneEvent) { state = updateState(_) }
“style fix”
![Page 92: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/92.jpg)
Eventsourced, recovery
/** MUST NOT SIDE-EFFECT! */!def receiveRecover = {! case replayedEvent: Event => ! updateState(replayedEvent)!}
![Page 93: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/93.jpg)
Eventsourced, snapshots
![Page 94: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/94.jpg)
Eventsourced, snapshots/** MUST NOT SIDE-EFFECT! */!def receiveRecover = {! case SnapshotOffer(meta, snapshot: State) => ! this.state = state!! case replayedEvent: Event => ! updateState(replayedEvent)!}
![Page 95: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/95.jpg)
Eventsourced, snapshots/** MUST NOT SIDE-EFFECT! */!def receiveRecover = {! case SnapshotOffer(meta, snapshot: State) => ! this.state = state!! case replayedEvent: Event => ! updateState(replayedEvent)!}
![Page 96: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/96.jpg)
Eventsourced, snapshots/** MUST NOT SIDE-EFFECT! */!def receiveRecover = {! case SnapshotOffer(meta, snapshot: State) => ! this.state = state!! case replayedEvent: Event => ! updateState(replayedEvent)!}
snapshot!? how?
![Page 97: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/97.jpg)
Eventsourced, snapshots
def receiveCommand = {! case command: Command =>! saveSnapshot(state) // async!!}
/** MUST NOT SIDE-EFFECT! */!def receiveRecover = {! case SnapshotOffer(meta, snapshot: State) => ! this.state = state!! case replayedEvent: Event => ! updateState(replayedEvent)!}
snapshot!? how?
![Page 98: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/98.jpg)
Snapshots
![Page 99: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/99.jpg)
Snapshots (in SnapshotStore)
![Page 100: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/100.jpg)
…sum of states…
Snapshots
!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
![Page 101: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/101.jpg)
state until [E8]
Snapshots
S8!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
![Page 102: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/102.jpg)
state until [E8]
Snapshots
S8
!
!
Snapshot Store
snapshot!
!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
![Page 103: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/103.jpg)
state until [E8]
Snapshots
S8
!
!
Snapshot Store
!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
S8
![Page 104: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/104.jpg)
state until [E8]
Snapshots
S8
!
!
Snapshot Store
!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
S8
crash!
![Page 105: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/105.jpg)
Snapshots
!
!
Snapshot Store
!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
S8
crash!
![Page 106: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/106.jpg)
“bring me up-to-date!”
Snapshots
!
!
Snapshot Store
!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
S8
restart!
![Page 107: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/107.jpg)
“bring me up-to-date!”
Snapshots
!
!
Snapshot Store
!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
S8
restart!replay!
![Page 108: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/108.jpg)
“bring me up-to-date!”
Snapshots
!
!
Snapshot Store
S8
restart!replay!
!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
![Page 109: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/109.jpg)
“bring me up-to-date!”
Snapshots
!
!
Snapshot Store
S8
restart!replay!
!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
![Page 110: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/110.jpg)
“bring me up-to-date!”
Snapshots
!
!
Snapshot Store
S8
restart!replay!
S8!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
![Page 111: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/111.jpg)
state until [E8]
Snapshots
!
!
Snapshot Store
S8
restart!replay!
S8!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
![Page 112: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/112.jpg)
state until [E8]
Snapshots
!
!
Snapshot Store
S8
S8!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
![Page 113: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/113.jpg)
state until [E8]
Snapshots
!
!
Snapshot Store
S8
S8!
!
Journal
E1 E2 E3 E4
E5 E6 E7 E8
We could delete these!
![Page 114: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/114.jpg)
trait MySummer extends Processor {! var nums: List[Int]! var total: Int! ! def receive = {! case "snap" => saveSnapshot(total)! case SaveSnapshotSuccess(metadata) => // ...! case SaveSnapshotFailure(metadata, reason) => // ...! }!}!
Snapshots, save
![Page 115: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/115.jpg)
trait MySummer extends Processor {! var nums: List[Int]! var total: Int! ! def receive = {! case "snap" => saveSnapshot(total)! case SaveSnapshotSuccess(metadata) => // ...! case SaveSnapshotFailure(metadata, reason) => // ...! }!}!
Snapshots, save
Async!
![Page 116: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/116.jpg)
trait MySummer extends Processor {! var nums: List[Int]! var total: Int! ! def receive = {! case "snap" => saveSnapshot(total)! case SaveSnapshotSuccess(metadata) => // ...! case SaveSnapshotFailure(metadata, reason) => // ...! }!}!
Snapshots, success
![Page 117: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/117.jpg)
trait MySummer extends Processor {! var nums: List[Int]! var total: Int! ! def receive = {! case "snap" => saveSnapshot(total)! case SaveSnapshotSuccess(metadata) => // ...! case SaveSnapshotFailure(metadata, reason) => // ...! }!}!
Snapshots, success
final case class SnapshotMetadata(! processorId: String, sequenceNr: Long, ! timestamp: Long)
![Page 118: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/118.jpg)
trait MySummer extends Processor {! var nums: List[Int]! var total: Int! ! def receive = {! case "snap" => saveSnapshot(total)! case SaveSnapshotSuccess(metadata) => // ...! case SaveSnapshotFailure(metadata, reason) => // ...! }!}!
Snapshots, success
![Page 119: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/119.jpg)
Snapshot Recovery
class Counter extends Processor {! var total = 0! ! def receive = {! case SnapshotOffer(metadata, snap: Int) => ! total = snap!! case Persistent(payload, sequenceNr) => // ...! }!}
![Page 120: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/120.jpg)
SnapshotsUpsides
• Simple!
• Faster recovery (!)
• Allows to delete “older” events
• “known state at point in time”
![Page 121: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/121.jpg)
SnapshotsDownsides
• More logic to write
• Maybe not needed if events replay “fast enough”
• Possibly “yet another database” to pick
• snapshots are different than events, may be big!
![Page 122: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/122.jpg)
Views
![Page 123: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/123.jpg)
Journal (DB)
!
!
!
Views!
Processor !
def processorId = “a” !
![Page 124: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/124.jpg)
Journal (DB)
!
!
!
Views!
Processor !
def processorId = “a” !
!View
!def processorId = “a”
!!!
![Page 125: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/125.jpg)
Journal (DB)
!
!
!
Views!
Processor !
def processorId = “a” !
!View
!def processorId = “a”
!!!
pooling
![Page 126: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/126.jpg)
Journal (DB)
!
!
!
Views!
Processor !
def processorId = “a” !
!View
!def processorId = “a”
!!!
pooling
!View
!def processorId = “a”
!!!
pooling
![Page 127: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/127.jpg)
Journal (DB)
!
!
!
Views!
Processor !
def processorId = “a” !
!View
!def processorId = “a”
!!!
pooling
!View
!def processorId = “a”
!!!
pooling
different ActorPath, same processorId
![Page 128: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/128.jpg)
View
class DoublingCounterProcessor extends View {! var state = 0! override val processorId = "counter"!! def receive = {! case Persistent(payload, seqNr) =>! // “state += 2 * payload” !! }!}
![Page 129: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/129.jpg)
Akka Persistence Plugins
![Page 130: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/130.jpg)
Akka Persistence PluginsPlugins are Community maintained!
(“not sure how production ready”)
http://akka.io/community/#journal_plugins
![Page 131: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/131.jpg)
Akka Persistence PluginsPlugins are Community maintained!
(“not sure how production ready”)
http://akka.io/community/#journal_plugins
• Journals - Cassandra, HBase, Mongo …
![Page 132: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/132.jpg)
Akka Persistence PluginsPlugins are Community maintained!
(“not sure how production ready”)
http://akka.io/community/#journal_plugins
• Journals - Cassandra, HBase, Mongo …
• SnapshotStores - Cassandra, HDFS, HBase, Mongo …
![Page 133: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/133.jpg)
SnapshotStore Plugins!
http://akka.io/community/#journal_plugins
![Page 134: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/134.jpg)
Links• Official docs: http://doc.akka.io/docs/akka/2.3.0/scala/persistence.html
• Patrik’s Slides & Webinar: http://www.slideshare.net/patriknw/akka-persistence-webinar
• Papers:
• Sagas http://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf
• Life beyond Distributed Transactions: http://www-db.cs.wisc.edu/cidr/cidr2007/papers/cidr07p15.pdf
• Pics:
• http://misaspuppy.deviantart.com/art/Messenger-s-Cutie-Mark-A-Flying-Envelope-291040459
![Page 135: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/135.jpg)
Mailing List
groups.google.com/forum/#!forum/akka-user
![Page 136: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/136.jpg)
LinksEric Evans - “the DDD book”
http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215 !!!!!!
Vaughn Vernon’s Book and Talk http://www.amazon.com/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577
video: https://skillsmatter.com/skillscasts/4698-reactive-ddd-with-scala-and-akka !!!
![Page 137: Akka persistence == event sourcing in 30 minutes](https://reader034.vdocuments.mx/reader034/viewer/2022042623/540decbc8d7f728d7e8b4b68/html5/thumbnails/137.jpg)
ktoso @ typesafe.com twitter: ktosopl github: ktoso blog: project13.pl team blog: letitcrash.com Scalar 2014 @ Warsaw, PL
!
Dzięki! Thanks! ありがとう! !
!
ping me: