@michaltomanski
class UserService {
createUser(User user): Unit
getUser(long id): User
}
class UserWriteService {
createUser(User user): Unit
}
class UserReadService {
getUser(long id): User
}
@michaltomanski
DeactivateUser(id = 1)
Is the user with id=1 active?
UserDeactivated(id = 1)
UserDeactivated(id = 1) goes to DB
isActive = false
Rare
Show to the user his:
Show to the user other people's:
class CounterActor extends Actor {
var counter = 0
def receive = {
case IncreaseCounter(amount: Int) => counter = counter + amount
case GimmeCounter => sender ! counter
case _ => println("Unknown message")
}
}
actor ! IncreaseCounter(5) // tell
actor ? GimmeCounter // ask
class PersistedCounter extends PersistentActor {
var counter = 0
override def receiveCommand: Receive = {
case increase: IncreaseCounter =>
persist(increase) { event =>
counter = counter + increase.amount
}
case GimmeCounter => sender ! counter
}
override def receiveRecover: Receive = {
case increase: IncreaseCounter => counter = counter + increase.amount
case RecoveryCompleted => println("Events recovery completed")
}
override def persistenceId: String = "persisted-counter"
}
Persistent Actor
Journal (Cassandra)
akka.persistence.journal.plugin = "cassandra-journal"
akka.persistence.snapshot-store.plugin = "cassandra-snapshot-store"
event-adapters = {
speedcuber = "com.mtomanski.timer.infrastructure
.akka.adapter.SpeedcuberEventsTaggingAdapter"
}
event-adapter-bindings = {
"com.mtomanski.timer.domain.model.Speedcuber" = [speedcuber]
}
class SpeedcuberEventsTaggingAdapter extends WriteEventAdapter {
override def toJournal(event: Any): Any = Tagged(event, Set("Speedcuber"))
...
}
Node 1
Node 2
Akka persistence query
Postgresql
val s = readJournal.eventsByTag("Speedcuber")
val s = readJournal.eventsByTag("Speedcuber", state.offset)
s.mapAsync(1)(handleEvent).runWith(Sink.foreach(offset => self ! SaveOffset(offset)))
def handleEvent = {
case EventEnvelope(offset, _, _, event: BestAvgChanged) =>
viewBuilder.upsertBestAvgView(BestAvg(event.user, event.millis)).map(_ => offset)
val s = readJournal.eventsByTag("Speedcuber", state.offset)
val s = readJournal.eventsByTag("Speedcuber", state.offset)
s.mapAsync(1)(handleEvent)
val s = readJournal.eventsByTag("Speedcuber", state.offset)
s.mapAsync(1)(handleEvent).runWith(Sink.foreach(offset => self ! SaveOffset(offset)))
The oldest node
¯\_(ツ)_/¯
@michaltomanski
slides.com/michaltomanski/cqrs
github.com/michaltomanski/cqrs-demo
vote.scalaua.com