Codemotion akka persistence, cqrs%2 fes y otras siglas del montón
-
Upload
javier-santos-paniego -
Category
Software
-
view
350 -
download
0
Transcript of Codemotion akka persistence, cqrs%2 fes y otras siglas del montón
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Akka persistence, CQRS/ES y otras siglas del montón
Javier Santos
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
About us
Javier Santos @jpaniego
«Hay dos formas de programar sin errores; sólo la tercera funciona»
Alan J Perlis
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
scalera.es
@scalerablog
About us
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem to solve
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem to solve
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem to solve
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
DDD - Domain Driven Design● Context - The setting in which a word or statement appears that
determines its meaning
● Domain (Ontology) - The subject area to which the user applies a program is the domain of the software
● Model - A system of abstractions that describes selected aspects of a domain and can be used to solve problems related to that domain
● Ubiquitous language - A language structured around the domain model and used by all team members to connect all the activities of the team with the software
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
DDD - Domain Driven Design● Context - The setting in which a word or statement appears that
determines its meaning
● Domain (Ontology) - The subject area to which the user applies a program is the domain of the software
● Model - A system of abstractions that describes selected aspects of a domain and can be used to solve problems related to that domain
● Ubiquitous language - A language structured around the domain model and used by all team members to connect all the activities of the team with the software
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
DDD - Domain Driven Design● Context - The setting in which a word or statement appears that
determines its meaning
● Domain (Ontology) - The subject area to which the user applies a program is the domain of the software
● Model - A system of abstractions that describes selected aspects of a domain and can be used to solve problems related to that domain
● Ubiquitous language - A language structured around the domain model and used by all team members to connect all the activities of the team with the software
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Our domain modelcase class User( name: String, address: Address, credit: Double, currentBike: Option[Id[Bike]])
case class Bike( model: String, battery: Bike.Battery.Status, bikeStation: Option[Id[Station]])
case class Address( street: String, number: String, zipCode: String)
case class Station( address: Address, maxBikeCapacity: Int, currentCapacity: Int)
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Anemic Domain Model
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Anemic Domain Model
● Think about a domain class that only contains fields and no methods.
● Anti-pattern against the main idea of the object-oriented programming paradigm: combining data and process together.
● Why not using C structs then? ¬¬
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Anemic Domain Modelcase class User( name: String, address: Address, credit: Double, currentBike: Option[Id[Bike]]) {
def startRental(bike: Id[Bike]): User = { require( currentBike.isEmpty,"There's another rental on course.") require( credit > 0, "Not enough credit to start a rental.") this.copy(currentBike = Some(bike)) }
//...
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Anemic Domain Model //...
def finishRental(creditExpense: Double): User = { require( currentBike.isDefined, "There is not a rental on course.") this.copy( currentBike = None, credit = credit - creditExpense) }
}
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Aggregate
● Cluster of domain objects
● The whole block of domain objects works as a sole entity.
User Bike
Address
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Aggregate
trait Aggregate extends ...{def id: Id[This]
}
case class Station( address: Address, maxBikeCapacity: Int, currentCapacity: Int)
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Event Sourcing● “Every change to the state of an application is captured in an event
object, and that these event objects are themselves stored in the sequence they were applied for the same lifetime as the application state itself” - Martin Fowler
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Event Sourcing● The system state is the sum of the events
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Stateful[T]trait Stateful[S] {
type This <: Stateful[S]
type Action = scalaz.State[S, Event]
val state: S
def apply(s: S): This
def update(f: Action): (This, E) = {val (newState, e) = f(state)(apply(newState), e)
}
}
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
scalaz.State
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
scalaz.State● Represents a state mutation that may generate some effect.
● type State[S, Effect] = StateT[Id, S, Effect]
● Monad = composable!
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
scalaz.State
type Action[E] = scalaz.State[User, E]
def addCredit(amount: Double): Action[Double] = State(user =>
(user.copy(credit = user.credit + amount), amount))
def rentBike(bike: Id[Bike], timesUsed: Long): Action[Long] = State(user =>
(user.copy(currentBike = Some(bike), timesUsed + 1))
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
scalaz.State
val topUpAndRent: Action[(Double, Long)] = for {
newCredit <- addCredit(10)timesUsed <- rentBike(Id(“bike-1”))
} yield (newCredit, timesUsed)
val (relaxingAnn, (newCredit, bikeAge)) = topUpAndRent(User(“Ann Bottle”, Addres(...), 0.0, None)
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Immutability perversion
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
StatefulAgg[T]trait StateAgg[S] extends Stateful[S]{
_: PersistenActor =>
var aggState: S
def updateState(f: Action): Unit = {val (newAgg, _) = update(f)aggState = newAgg.state
}
}
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
StatefulAgg[T]trait StateAgg[S] extends Stateful[S]{
_: PersistenActor =>
var aggState: S
def updateState(f: Action): Unit = {val (newAgg, _) = update(f)aggState = newAgg.state
}
}
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
CQRS
● Command Query Responsibility Segregation
● Main idea: “You can use a different model to update the information than the model you use to read information”
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
CQRS
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
CQRS
trait Command[Agg] {val to: Id[Agg]
}
trait Event
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Akka persistence● End of 2013
● Martin Krasser
● Main idea : store the internal state of an actor
● ...BUT not directly, only through the changes the actor has suffered.
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Akka persistence
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Akka persistence
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Akka persistence - Failure recovery
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Akka persistence - Failure recovery
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Akka persistence - Failure recovery
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Akka persistence - Failure recovery
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Snapshotting
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Snapshotting
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Persistent actors
trait PersistenceStuff extends PersistentActor{
def persistenceId: String
def receiveCommand: Receive
def receiveRecover: Receive
}
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
trait Reactive { _: Stateful[S] =>
val onEvent: Event => Action
val onSnapshot: Any => Action = {case snapshot => State(s => (s, snapshot))
}
def asEvent(c: Command[This]): Event
}
Persistent actors
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
trait Aggregate[S] extends StateAgg[S] with Reactive with PersistentActor {def persistenceId: String = id.iddef receiveRecover: Receive = {
case event: Event => update(onEvent(event))case snapshot => update(onSnapshot(snapshot))
}def receiveCommand: Receive = {
case cmd: Command[This@unchecked] =>if (checkState(cmd))
persistAll(List(asEvent(cmd))){ event =>update(onEvent(event))sender ! event
}else sender ! Nope
}}
Persistent actors
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
case class UserAgg(state: User.Default) extends Aggregate[User] {
type This = UserAggdef apply(s: User) = UserAgg(s)
override val onEvent = {case e@AddedCredit(amount) =>
State(s => (s.copy(credit = s.credit+10),e))}
def asEvent(c: Command[This]): Event = c match {case AddCredit(_, amount) => AddedCredit(amount)
}
}
Persistent actors
boilerplate
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Persistence Queries● Like a PersistentActor but with no Command part
● peristenceId subscription or by tag
● refreshInterval in most plugins
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Persistence Queriesval readJournal =
PersistenceQuery(system).readJournalFor[MyScaladslReadJournal]("akka.persistence.query.my-read-journal")
// issue query to journalval source: Source[EventEnvelope, NotUsed] =
readJournal.eventsByPersistenceId("user-1337")
// materialize stream, consuming eventsimplicit val mat = ActorMaterializer()source.runForeach { event => println("Event: " + event) }
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
getOrCreate● A new command is launched against user1...how to deliver it?
● Managers
class UserManager extends Actor {override def receive = {
case cmd: Command[User@unchecked] =>getOrCreate(cmd.addresse) forward cmd
}}
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
getOrCreate
● Imagine there are 500k registered users …
● ...you create a persistent actor and deliver the just arrived command…
● ...the actor keeps alive….
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
getOrCreate
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Passivation
● Kill the actor if it’s iddle…
trait Passivation extends PersistentActor {import ShardRegion.Passivatecontext.setReceiveTimeout(60.seconds)override def receiveCommand = {
//…case ReceiveTimeout =>
context.parent ! Passivate(stopMessage = Stop)}
}
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Journal● Different supported pluggable storages:
○ AndroidSQLite○ BDB○ Cassandra○ Kafka○ DynamoDB○ HBase○ MongoDB○ …
● Why choosing a distributed database as Journal for High Availability?
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
HA : Clustering
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
HA : Clustering● Send semantics
○ Send: The message is sent to an only cluster actor that fits in the path. If several, the actor is chosen randomly.
clusterClient ! Send(«/user/handler», «hello», localAffinity = true)
○ SendToAll: The message is sent to all cluster actors that fit in the path.
clusterClient ! SendToAll(«/user/handler», «hi»)
○ Publish: The message is sent to all topic subscribers
clusterClient ! Publish(«myTopic», «hello»)
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Partitioning : Sharding● Study case:
○ A command is sent (in a cluster) to aggregate1, the actor is created in node1, the command is processed.
○ A second command is sent (in the same cluster) to aggregate1, but this time the command is received by node2, so the actor is created there.
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Partitioning : Sharding● It provides
○ Load balancing: In case a new is added to the cluster or another one crashes, the total actor amount is balanced.
○ Unique actor identification, so messages that are sent to the same actor will be forwarded to the node that handles in that moment that shard id (previous case won’t take place)
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Partitioning : Sharding
● Semantics
○ Entry: It represents a uniquely identified entity with persistent state (Aggregate).
○ Shard: Entry group. Experts recommend
ShardAmount = 10 x MaxNodeAmount
○ ShardRegion: Same Entry shard group.
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Partitioning : Sharding
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
#graciasCarmena#llevameEnTuBiciceta
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem: eventual consistency● Changes may take
some time
● If the user requires an immediate update, user views could be updated when the command part has acknowledge the event persistence (handle it carefully...)
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem: Distributed transactions
● Let’s suppose credit transfer is allowed among users
● Saga Pattern: A persistent actor that represents a transaction among aggregates
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem: Distributed transactions
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem: cluster initialization
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem: cluster initialization
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem: cluster initialization
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem: cluster initialization
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem: Testing a PersistentActor
TestActorRef + PersistentActor =
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem: Testing a PersistentActor
TestActorRef + PersistentActor =
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Problem : Schema evolution● Imagine the following:
case class User(name: String, address: Address, amount: Double)
case class User(cardId: String, amount: Double, usualBike: Bike)
● Choosing proper serializers are the key (ProtoBuf, Avro, Java ser...no way)
● Event migration (EventAdapter)● How to:
○ add attribute○ remove attribute○ rename attribute○ ...
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Lightbend approach
● Runs on JRE 1.8
● Multiple service control
● Event stream abstraction
● Easy scalability
● Java SDK ...
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Lightbend approach
● Runs on JRE 1.8
● Multiple service control
● Event stream abstraction
● Easy scalability
● Java SDK ...
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
scalera.es
@scalerablog
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
MADRID · NOV 18-19 · 2016
Scala Programming @ Madrid
Akka persistence, CQRS/ES y otras siglas del montón
Javier Santos