Codemotion akka persistence, cqrs%2 fes y otras siglas del montón

Post on 11-Apr-2017

349 views 0 download

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