event sourcing composable with monads€¦ · composition in event sourcing composing event...

115
COMPOSABLE EVENT SOURCING WITH MONADS Daniel Krzywicki @eleaar Lambda Days 2018-02-22

Upload: others

Post on 23-Jun-2020

10 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

COMPOSABLEEVENT SOURCINGWITH MONADSDaniel Krzywicki @eleaar

Lambda Days 2018-02-22

Page 2: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

2//

The following is based

on a true story

Page 3: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

3//

01 //

02 //

03 //

04 //

Outline

Minimal model for event sourcing

The problem of composition

The Functional approach

Further possibilities

Page 4: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

4//

Introduction to Event Sourcing

Page 5: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

5//

Instead of storing state,

store changes to the state

Page 6: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

6//

Introduction :: event store

Events

n

0Event store

Changes to the system state are reified as eventsand appended to an event store.

Page 7: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

7//

Introduction :: replaying state

Events

n

0Event store

The system state is said to be projected/replayed from the storeusing event handlers

State

Page 8: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

8//

Introduction :: partial replays

n

2

0Event store

We can easily replay only a part of the events to know the state of the system at any point in time

State(tn)

State(t2)

Page 9: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

9//

What the talk is not about

- Event sourcing frameworks- Infrastructure (Kafka, MongoDB, …)- Architecture (Event Store, sharding, partitioning)- Error handling

Page 10: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

10//

What the talk is about

Functional => Focus on composabilityProgramming => Focus on domain modeling and dev API

Page 11: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

11//

01 // Minimal Model for Event Sourcing

Page 12: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

12//

01.1 //Modeling the domain - Turtles!

Page 13: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

13//

case class Turtle(id: String, pos: Position, dir: Direction)

object Turtle {

def create(id: String, pos: Position, dir: Direction): Either[String, Turtle]

def turn(rot: Rotation)(turtle: Turtle): Either[String, Turtle]

def walk(dist: Int)(turtle: Turtle): Either[String, Turtle]

}

Domain model

Page 14: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

14//

def create(id: String, pos: Position, dir: Direction): Either[String, Turtle] = if (tooFarAwayFromOrigin(pos)) Left("Too far away") else Right(Turtle(id, pos, dir))

Domain logic

Page 15: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

15//

Basic model - demo

def walkRight(dist: Int)(state: Turtle) = for { state1 <- Turtle.walk(dist)(state) state2 <- Turtle.turn(ToRight)(state1)} yield state2

val state = for { state1 <- Turtle.create("123", Position.zero, North) state2 <- walkRight(1)(state1) state3 <- walkRight(1)(state2) state4 <- walkRight(2)(state3) state5 <- walkRight(2)(state4)} yield state5

state shouldBe Right(Turtle("123", Position(-1, -1), North))

Page 16: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

16//

Basic model - demo

def walkRight(dist: Int)(state: Turtle) = for { state1 <- Turtle.walk(dist)(state) state2 <- Turtle.turn(ToRight)(state1)} yield state2

val state = for { state1 <- Turtle.create("123", Position.zero, North) state2 <- walkRight(1)(state1) state3 <- walkRight(1)(state2) state4 <- walkRight(2)(state3) state5 <- walkRight(2)(state4)} yield state5

state shouldBe Right(Turtle("123", Position(-1, -1), North))

Page 17: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

17//

Basic model - demo

def walkRight(dist: Int)(state: Turtle) = for { state1 <- Turtle.walk(dist)(state) state2 <- Turtle.turn(ToRight)(state1)} yield state2

val state = for { state1 <- Turtle.create("123", Position.zero, North) state2 <- walkRight(1)(state1) state3 <- walkRight(1)(state2) state4 <- walkRight(2)(state3) state5 <- walkRight(2)(state4)} yield state5

state shouldBe Right(Turtle("123", Position(-1, -1), North))

Page 18: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

18//

Basic model - demo

def walkRight(dist: Int)(state: Turtle) = for { state1 <- Turtle.walk(dist)(state) state2 <- Turtle.turn(ToRight)(state1)} yield state2

val state = for { state1 <- Turtle.create("123", Position.zero, North) state2 <- walkRight(1)(state1) state3 <- walkRight(1)(state2) state4 <- walkRight(2)(state3) state5 <- walkRight(2)(state4)} yield state5

state shouldBe Right(Turtle("123", Position(-1, -1), North))

Page 19: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

19//

Basic model - demo

val state = for { state1 <- Turtle.create("123", Position.zero, North) state2 <- walkRight(1)(state1) state3 <- walkRight(1)(state2) state4 <- walkRight(2)(state3) state5 <- walkRight(2)(state4)} yield state5

Page 20: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

20//

Basic model - demo

// We have to propagate the state manually - verbose and error-prone

val state = for { state1 <- Turtle.create("123", Position.zero, North) state2 <- walkRight(1)(state1) state3 <- walkRight(1)(state2) state4 <- walkRight(2)(state3) state5 <- walkRight(2)(state4)} yield state5

Page 21: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

21//

// We can flatMap to avoid passing the state explicitly

val state = Turtle.create("123", Position.zero, North) .flatMap(walkRight(1)) .flatMap(walkRight(1)) .flatMap(walkRight(2)) .flatMap(walkRight(2))

Basic model - demo

Page 22: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

22//

01.2 //Event sourcing the domain

Page 23: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

23//

Modeling events

// We can represent the result of our commands as eventssealed trait TurtleEvent { def id: String }

case class Created(id: String, pos: Position, dir: Direction) extends TurtleEventcase class Turned(id: String, rot: Rotation) extends TurtleEventcase class Walked(id: String, dist: Int) extends TurtleEvent

Page 24: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

24//

type EventHandler[STATE, EVENT] = (Option[STATE], EVENT) => Some[STATE]

val handler1: EventHandler[Turtle, TurtleEvent] = { case (None, Created(id, pos, dir)) => Some(Turtle(id, pos, dir)) case (Some(turtle), Turned(id, rot)) if id == turtle.id => Some(turtle.copy(dir = Direction.rotate(turtle.dir, rot))) case (Some(turtle), Walked(id, dist)) if id == turtle.id => Some(turtle.copy(pos = Position.move(turtle.pos, turtle.dir, dist))) case (event, state) => sys.error(s"Invalid event $event for state $state")}

Event handler for creation events

Page 25: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

25//

type EventHandler[STATE, EVENT] = (Option[STATE], EVENT) => Some[STATE]

val handler1: EventHandler[Turtle, TurtleEvent] = { case (None, Created(id, pos, dir)) => Some(Turtle(id, pos, dir)) case (Some(turtle), Turned(id, rot)) if id == turtle.id => Some(turtle.copy(dir = Direction.rotate(turtle.dir, rot))) case (Some(turtle), Walked(id, dist)) if id == turtle.id => Some(turtle.copy(pos = Position.move(turtle.pos, turtle.dir, dist))) case (event, state) => sys.error(s"Invalid event $event for state $state")}

Event handler for creation events

Page 26: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

26//

type EventHandler[STATE, EVENT] = (Option[STATE], EVENT) => Some[STATE]

val handler1: EventHandler[Turtle, TurtleEvent] = { case (None, Created(id, pos, dir)) => Some(Turtle(id, pos, dir)) case (Some(turtle), Turned(id, rot)) if id == turtle.id => Some(turtle.copy(dir = Direction.rotate(turtle.dir, rot))) case (Some(turtle), Walked(id, dist)) if id == turtle.id => Some(turtle.copy(pos = Position.move(turtle.pos, turtle.dir, dist))) case (event, state) => sys.error(s"Invalid event $event for state $state")}

Event handler for creation events

Page 27: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

27//

type EventHandler[STATE, EVENT] = (Option[STATE], EVENT) => Some[STATE]

val handler1: EventHandler[Turtle, TurtleEvent] = { case (None, Created(id, pos, dir)) => Some(Turtle(id, pos, dir)) case (Some(turtle), Turned(id, rot)) if id == turtle.id => Some(turtle.copy(dir = Direction.rotate(turtle.dir, rot))) case (Some(turtle), Walked(id, dist)) if id == turtle.id => Some(turtle.copy(pos = Position.move(turtle.pos, turtle.dir, dist))) case (event, state) => sys.error(s"Invalid event $event for state $state")}

Event handler for creation events

Page 28: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

28//

type EventHandler[STATE, EVENT] = (Option[STATE], EVENT) => Some[STATE]

val handler1: EventHandler[Turtle, TurtleEvent] = { case (None, Created(id, pos, dir)) => Some(Turtle(id, pos, dir)) case (Some(turtle), Turned(id, rot)) if id == turtle.id => Some(turtle.copy(dir = Direction.rotate(turtle.dir, rot))) case (Some(turtle), Walked(id, dist)) if id == turtle.id => Some(turtle.copy(pos = Position.move(turtle.pos, turtle.dir, dist))) case (event, state) => sys.error(s"Invalid event $event for state $state")}

Event handler for creation events

Page 29: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

29//

type EventHandler[STATE, EVENT] = (Option[STATE], EVENT) => Some[STATE]

val handler1: EventHandler[Turtle, TurtleEvent] = { case (None, Created(id, pos, dir)) => Some(Turtle(id, pos, dir)) case (Some(turtle), Turned(id, rot)) if id == turtle.id => Some(turtle.copy(dir = Direction.rotate(turtle.dir, rot)) case (Some(turtle), Walked(id, dist)) if id == turtle.id => Some(turtle.copy(pos = Position.move(turtle.pos, turtle.dir, dist))) case (event, state) => sys.error(s"Invalid event $event for state $state")}

Event handler for creation events

Page 30: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

30//

type EventHandler[STATE, EVENT] = (Option[STATE], EVENT) => Some[STATE]

val handler1: EventHandler[Turtle, TurtleEvent] = { case (None, Created(id, pos, dir)) => Some(Turtle(id, pos, dir)) case (Some(turtle), Turned(id, rot)) if id == turtle.id => Some(turtle.copy(dir = Direction.rotate(turtle.dir, rot))) case (Some(turtle), Walked(id, dist)) if id == turtle.id => Some(turtle.copy(pos = Position.move(turtle.pos, turtle.dir, dist))) case (event, state) => sys.error(s"Invalid event $event for state $state")}

Event handler for creation events

Page 31: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

31//

val initialState = Option.empty[Turtle]val events = Seq( Created("123", Position.zero, North), Walked("123", 1), Turned("123", ToRight),)

val finalState = events.foldLeft(initialState)(handler0).valuefinalState shouldBe Turtle("123", Position(0, 1), Est)

Event handler usage :: demo

Page 32: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

32//

val initialState = Option.empty[Turtle]val events = Seq( Created("123", Position.zero, North), Walked("123", 1), Turned("123", ToRight),)

val finalState = events.foldLeft(initialState)(handler0).valuefinalState shouldBe Turtle("123", Position(0, 1), Est)

Event handler usage :: demo

Page 33: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

33//

val initialState = Option.empty[Turtle]val events = Seq( Created("123", Position.zero, North), Walked("123", 1), Turned("123", ToRight),)

val finalState = events.foldLeft(initialState)(handler0).valuefinalState shouldBe Turtle("123", Position(0, 1), Est)

Event handler usage :: demo

Page 34: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

34//

Event handler usage :: demo

val initialState = Option.empty[Turtle]val events = Seq( Created("123", Position.zero, North), Walked("123", 1), Turned("123", ToRight),)

val finalState = events.foldLeft(initialState)(handler0).valuefinalState shouldBe Turtle("123", Position(0, 1), Est)

Page 35: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

35//

Event handler usage :: demo

val initialState = Option.empty[Turtle]val events = Seq( Created("123", Position.zero, North), Walked("123", 1), Turned("123", ToRight),)

val finalState = events.foldLeft(initialState)(handler0).valuefinalState shouldBe Turtle("123", Position(0, 1), Est)

Page 36: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

36//

Event handler usage :: demo

val initialState = Option.empty[Turtle]val events = Seq( Created("123", Position.zero, North), Walked("123", 1), Turned("123", ToRight),)

val finalState = events.foldLeft(initialState)(handler0).valuefinalState shouldBe Turtle("123", Position(0, 1), Est)

Page 37: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

37//

// There is some boilerplate when defining the handlerval handler0: EventHandler[Turtle, TurtleEvent] = { case (None, Created(id, pos, dir)) => Some(Turtle(id, pos, dir)) case (Some(t), Turned(id, rot)) if id == t.id => Some(t.copy(dir = Direction.rotate(t.dir, rot))) case (Some(t), Walked(id, dist)) if id == t.id => Some(t.copy(pos = Position.move(t.pos, t.dir, dist))) case (event, state) => sys.error(s"Invalid event $event for state $state")}

Syntactic sugar for handler definition

Page 38: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

38//

// We can use a factory to reduce boilerplate and have a neat final handler

val handler = EventHandler[Turtle, TurtleEvent] { case (None, Created(id, pos, dir)) => Turtle(id, pos, dir) case (Some(t), Turned(id, rot)) if id == t.id => t.copy(dir = Direction.rotate(t.dir, rot)) case (Some(t), Walked(id, dist)) if id == t.id => t.copy(pos = Position.move(t.pos, t.dir, dist))}

Syntactic sugar for handler definition

Page 39: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

39//

// Without event sourcingdef create(id: String, pos: Position, dir: Direction): Either[String, Turtle] = if (tooFarAwayFromOrigin(pos)) Left("Too far away") else Right(Turtle(id, pos, dir))

// With event sourcingdef create(id: String, pos: Position, dir: Direction) = if (tooFarAwayFromOrigin(pos)) Left("Too far away") else Right(Created(id, pos, dir))

// in the handler case (None, Created(id, pos, dir)) => Turtle(id, pos, dir)

Domain logic - revisited

Page 40: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

40//

What we have seen so far

Page 41: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

41//

What we have seen so far

- modeling the domain- defining events and event handlers

Page 42: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

42//

What more could we want?

Page 43: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

43//

Page 44: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

44//

02 // The problem of composition

Page 45: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

45//

Composition in event sourcing

Composing event handlers is easy - they’re just plain functions

Composing commands is less trivial - what events should we create?

Page 46: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

46//

Why would we want to

compose commands

in the first place?

Page 47: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

47//

Basic model - demo

// Remember this one in the basic model? It’s actually a composite command

def walkRight(dist: Int)(state: Turtle) = for { state1 <- Turtle.walk(dist)(state) state2 <- Turtle.turn(ToRight)(state1)} yield state2

// How do we event source it?

Page 48: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

48//

How about these?

def turnAround()(turtle: Turtle): Either[String, Turtle] = ???

def makeUTurn(radius: Int)(turtle: Turtle): Either[String, Turtle] = ???

Composing commands

Page 49: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

49//

// The CISC approach: let’s just create more event types

// So far we hadcreate ---> Createdwalk ---> Walkedturn ---> Turned

// So that would give uswalkRight ---> WalkedRightturnAround ---> TurnedAroundmakeUTurn ---> MadeUTurn

Composing commands

Page 50: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

50//

// The CISC approach: let’s just create more event types

// So far we hadcreate ---> Createdwalk ---> Walkedturn ---> Turned

// So that would give uswalkRight ---> WalkedRightturnAround ---> TurnedAroundmakeUTurn ---> MadeUTurn

// Problem: extensivity

Composing commands

Page 51: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

51//

// Consider we might have additional handlersdef turtleTotalDistance(id: String): EventHandler[Int, TurtleEvent] = { case (None, Created(turtleId, _, _)) if id == turtleId => Some(0) case (Some(total), Walked(turtleId, dist)) if id == turtleId => Some(total + dist) case (maybeTotal, _) => maybeTotal}

Composing commands

Page 52: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

52//

// Adding new event types forces up to update every possible interpreterdef turtleTotalDistance(id: String): EventHandler[Int, TurtleEvent] = { case (None, Created(turtleId, _, _)) if id == turtleId => Some(0) case (Some(total), Walked(turtleId, dist)) if id == turtleId => Some(total + dist) case (Some(total), WalkedRight(turtleId, dist)) if id == turtleId => Some(total + dist) case (Some(total), MadeUTurn(turtleId, radius)) if id == turtleId => Some(total + 3 * radius) case (maybeTotal, _) => maybeTotal}

Composing commands

Page 53: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

53//

Events with overlapping

semantics are leaky

Page 54: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

54//

How about composition?

Page 55: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

55//

// The RISC approach: let’s compose existing event types

// So far we hadcreate ---> Createdwalk ---> Walkedturn ---> Turned

// So that would give uswalkRight ---> Walked + TurnedturnAround ---> Turned + TurnedmakeUTurn ---> Walked + Turned + Walked + Turned + Walked

Composing commands

Page 56: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

56//

// That’s what we did without event sourcing: composition

def walkRight(dist: Int)(state: Turtle) = for { state1 <- Turtle.walk(dist)(state) state2 <- Turtle.turn(ToRight)(state1)} yield state2

// Why should it be any different now?

Composing commands

Page 57: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

57//

02.1 //Dealing with multiple events

Page 58: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

58//

// So how could we try to compose this:

def walkRight(dist: Int)(state: Turtle) = for { event1 <- Turtle.walk(dist)(state) event2 <- Turtle.turn(ToRight)(???)} yield ???

Composing commands

Page 59: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

59//

// We need a state here

def walkRight(dist: Int)(state: Turtle) = for { event1 <- Turtle.walk(dist)(state) event2 <- Turtle.turn(ToRight)(???)} yield ???

Composing commands

Page 60: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

60//

// We can use our handler to replay the first event

def walkRight(dist: Int)(state: Turtle) = for { event1 <- Turtle.walk(dist)(state) state2 = Turtle.handler(Some(state), event1).value event2 <- Turtle.turn(ToRight)(state2)} yield ???

Composing commands

Page 61: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

61//

// We can use our handler to replay the first event

def walkRight(dist: Int)(state: Turtle) = for { event1 <- Turtle.walk(dist)(state) state2 = Turtle.handler(Some(state), event1).value event2 <- Turtle.turn(ToRight)(state2)} yield ???

Composing commands

Page 62: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

62//

// We’ll need to return both events

def walkRight(dist: Int)(state: Turtle) = for { event1 <- Turtle.walk(dist)(state) state2 = Turtle.handler(Some(state), event1).value event2 <- Turtle.turn(ToRight)(state2)} yield Seq(event1, event2)

Composing commands

Page 63: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

63//

Persisting multiple events

atomically

Page 64: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

64//

Event journal - revisited

// Obviously, we’ll need to be able to persist multiple events together

trait WriteJournal[EVENT] { // Saving the batch of events must be atomic def persist(events: Seq[EVENT]): Future[Unit]}

Page 65: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

65//

Persisting multiple events

Persisting multiple events may seem odd to some.

Others do that as well: Greg Young’s Event Store has a concept of atomic “commits” which contain multiple events.Akka Persistence API allows to persist multiple events at once, as long as the journal supports it

Page 66: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

66//

Are we good already?

Page 67: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

67//

02.2 //The limits of an imperative approach

Page 68: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

68//

// This imperative approach...for { event1 <- Turtle.create("123", zero, North) state1 = Turtle.handler(None, event1).value event2 <- Turtle.walk(1)(state1)} yield Seq(event1, event2)

An imperative approach problems

Page 69: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

69//

// This imperative approach… does not scale!for { event1 <- Turtle.create("123", zero, North) state1 = Turtle.handler(None, event1).value event2 <- Turtle.walk(1)(state1) state2 = Turtle.handler(Some(state1), event2).value event3 <- Turtle.walk(1)(state2) state3 = Turtle.handler(Some(state2), event3).value event4 <- Turtle.walk(1)(state3) state4 = Turtle.handler(Some(state3), event4).value event5 <- Turtle.walk(1)(state4) state5 = Turtle.handler(Some(state4), event5).value event6 <- Turtle.walk(1)(state5)} yield Seq(event1, event2, event3, event4, event5, event6)

An imperative approach problems:: does not scale

Page 70: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

70//

// We need to manually replay at each stepfor { event1 <- Turtle.create("123", zero, North) state1 = Turtle.handler(None, event1).value event2 <- Turtle.walk(1)(state1) state2 = Turtle.handler(Some(state1), event2).value event3 <- Turtle.walk(1)(state2) state3 = Turtle.handler(Some(state2), event3).value event4 <- Turtle.walk(1)(state3) state4 = Turtle.handler(Some(state3), event4).value event5 <- Turtle.walk(1)(state4) state5 = Turtle.handler(Some(state4), event5).value event6 <- Turtle.walk(1)(state5)} yield Seq(event1, event2, event3, event4, event5, event6)

An imperative approach problems :: replaying events

Page 71: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

71//

// Accumulating events - so error-prone!for { event1 <- Turtle.create("123", zero, North) state1 = Turtle.handler(None, event1).value event2 <- Turtle.walk(1)(state1) state2 = Turtle.handler(Some(state1), event2).value event3 <- Turtle.walk(1)(state2) state3 = Turtle.handler(Some(state2), event3).value event4 <- Turtle.walk(1)(state3) state4 = Turtle.handler(Some(state3), event4).value event5 <- Turtle.walk(1)(state4) state5 = Turtle.handler(Some(state4), event5).value event6 <- Turtle.walk(1)(state5)} yield Seq(event1, event2, event3, event4, event5, event6)

An imperative approach problems:: accumulating events

Page 72: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

72//

// Propagating events and state - repetitive and so error-pronefor { event1 <- Turtle.create("123", zero, North) state1 = Turtle.handler(None, event1).value event2 <- Turtle.walk(1)(state1) state2 = Turtle.handler(Some(state1), event2).value event3 <- Turtle.walk(1)(state2) state3 = Turtle.handler(Some(state2), event3).value event4 <- Turtle.walk(1)(state3) state4 = Turtle.handler(Some(state3), event4).value event5 <- Turtle.walk(1)(state4) state5 = Turtle.handler(Some(state4), event5).value event6 <- Turtle.walk(1)(state5)} yield Seq(event1, event2, event3, event4, event5, event6)

An imperative approach problems:: propagating events and state

Page 73: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

73//

03 // A functional approach

Page 74: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

74//

Problems we need to solve yet when composing commands:

- replaying intermediate events- accumulating new events- propagating new state

Quick recap - Problems left

Page 75: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

75//

03.1 //Replaying events automatically

Page 76: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

76//

def walkRight(dist: Int)(state: Turtle) = for { event1 <- Turtle.walk(dist)(state) state1 = Turtle.handler(Some(state), event1).value event2 <- Turtle.turn(ToRight)(state1)} yield Seq(event1, event2)

for { event1 <- Turtle.create("123", Position.zero, North) state1 = Turtle.handler(None, event1).value events2 <- walkRight(1)(state1) state2 = events.foldLeft(Some(state1))(Turtle.handler).value events3 <- walkRight(1)(state2)} yield event1 +: events2 ++ events2

Replaying events manually - recap

Page 77: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

77//

def walkRight(dist: Int)(state: Turtle) = for { event1 <- Turtle.walk(dist)(state) state1 = Turtle.handler(Some(state), event1).value event2 <- Turtle.turn(ToRight)(state1)} yield Seq(event1, event2)

for { event1 <- Turtle.create("123", Position.zero, North) state1 = Turtle.handler(None, event1).value events2 <- walkRight(1)(state1) state2 = events.foldLeft(Some(state1))(Turtle.handler).value events3 <- walkRight(1)(state2)} yield event1 +: events2 ++ events2

Replaying events manually - recap

Page 78: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

78//

What could we do

to automate this?

Page 79: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

79//

// Let’s use helpers to compute the new state along with every new event

def sourceNew(block: Either[String, TurtleEvent]) = block.map { event => event -> Turtle.handler(None, event).value }

def source(block: Turtle => Either[String, TurtleEvent]) = (state: Turtle) => block(state).map { event => event -> Turtle.handler(Some(state), event).value }

Replaying events automatically with helpers

Page 80: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

80//

// Let’s use helpers to compute the new state along with every new event

def sourceNew(block: Either[String, TurtleEvent]) = block.map { event => event -> Turtle.handler(None, event).value }

def source(block: Turtle => Either[String, TurtleEvent]) = (state: Turtle) => block(state).map { event => event -> Turtle.handler(Some(state), event).value }

Replaying events automatically with helpers

Page 81: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

81//

// These helpers only “lift” creation and update functions

def sourceNew: Either[String, TurtleEvent] => Either[String, (TurtleEvent, Turtle)]

def source: (Turtle => Either[String, TurtleEvent]) => (Turtle => Either[String, (TurtleEvent, Turtle)])

Replaying events automatically with helpers - types

Page 82: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

82//

// These helpers only “lift” creation and update functions

def sourceNew: Either[String, TurtleEvent] => Either[String, (TurtleEvent, Turtle)]

def source: (Turtle => Either[String, TurtleEvent]) => (Turtle => Either[String, (TurtleEvent, Turtle)])

Replaying events automatically with helpers - types

Page 83: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

83//

// Before: manually replaying state

def walkRight(dist: Int)(state: Turtle) = for { event1 <- Turtle.walk(dist)(state) state1 = Turtle.handler(Some(state), event1).value event2 <- Turtle.turn(ToRight)(state1)} yield Seq(event1, event2)

Replaying events automatically with helpers - comparison

Page 84: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

84//

// Before: manually replaying state

def walkRight(dist: Int)(state: Turtle) = for { event1 <- Turtle.walk(dist)(state) state1 = Turtle.handler(Some(state), event1).value event2 <- Turtle.turn(ToRight)(state1)} yield Seq(event1, event2)

// After: automatically replaying state

def walkRight(dist: Int)(state: Turtle) = for { (event1, state1) <- source(Turtle.walk(dist))(state) (event2, state2) <- source(Turtle.turn(ToRight))(state1)} yield (Seq(event1, event2), state2)

Replaying events automatically with helpers - comparison

Page 85: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

85//

// Our example rewritten using the helper functions

def walkRight(dist: Int)(state: Turtle) = for { (event1, state1) <- source(Turtle.walk(dist))(state) (event2, state2) <- source(Turtle.turn(ToRight))(state1)} yield (Seq(event1, event2), state2)

for { (event1, state1) <- sourceNew(Turtle.create("123", Position.zero, North)) (events2, state2) <- walkRight(1)(state1) (events3, state3) <- walkRight(1)(state2)} yield (event1 +: events2 ++ events2, state3)

Replaying events automatically with helpers - demo

Page 86: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

86//

Problems we need to solve yet when composing commands:

- replaying previous events- accumulating new events- propagating new state

Problems left

Page 87: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

87//

// We still need to emit events in the right order at the end

for { (event1, state1) <- sourceNew(Turtle.create("123", Position.zero, North)) (events2, state2) <- walkRight(1)(state1) (events3, state3) <- walkRight(1)(state2)} yield (event1 +: events2 ++ events2, state3)

// What if we could accumulate them at each step of the for-comprehension?

Page 88: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

88//

03.2 //Accumulating events automatically

Page 89: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

89//

// Remember our helper?

def sourceNew: Either[String, TurtleEvent] => Either[String, (TurtleEvent, Turtle)]

Sourced class

Page 90: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

90//

// Remember our helper?

def sourceNew: Either[String, TurtleEvent] => Either[String, (TurtleEvent, Turtle)]

// We wrap our result into a case class, so that we try to write a flatMap

case class Sourced[STATE, EVENT](run: Either[String, (Seq[EVENT], STATE)])

// We use a Seq as we will be accumulating events

Sourced class

Page 91: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

91//

case class Sourced[STATE, EVENT](run: Either[String, (Seq[EVENT], STATE)] { def events: Either[String, Seq[EVENT]] = run.map { case (events, _) => events }

}

Sourced class

Page 92: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

92//

case class Sourced[STATE, EVENT](run: Either[String, (Seq[EVENT], STATE)] { def events: Either[String, STATE] = run.map { case (events, _) => events }

def flatMap[B](fn: STATE => Sourced[B, EVENT]): Sourced[B, EVENT] = Sourced[B, EVENT]( for { (currentEvents, currentState) <- this.run (newEvents, newState ) <- fn(currentState).run } yield (currentEvents ++ newEvents, newState) )

}

Sourced class

Page 93: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

93//

object Sourced { def pure[STATE, EVENT](state: STATE): Sourced[STATE, EVENT] = Sourced[STATE, EVENT](Right(Nil -> state))

}

Sourced class

Page 94: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

94//

Writer monad

Sourced[STATE, EVENT]

// is equivalent to

WriterT[Either[String, ?], Seq[EVENT], STATE]

Page 95: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

95//

// We can update our helpers. They really feel like “lifting” now.

def sourceNew: Either[String, TurtleEvent] => Sourced[Turtle, TurtleEvent] def source: (Turtle => Either[String, TurtleEvent]) =>

(Turtle => Sourced[Turtle, TurtleEvent])

Sourced monad

Page 96: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

96//

Sourced monad

// Event sourcing with the Sourced monad

def walkRight(dist: Int)(state: Turtle) = for { state1 <- source(Turtle.walk(dist))(state) state2 <- source(Turtle.turn(ToRight))(state1)} yield state2

Page 97: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

97//

Sourced monad

// Event sourcing with the Sourced monad

def walkRight(dist: Int)(state: Turtle) = for { state1 <- source(Turtle.walk(dist))(state) state2 <- source(Turtle.turn(ToRight))(state1)} yield state2

// Without event sourcing

def walkRight(dist: Int)(state: Turtle) = for { state1 <- Turtle.walk(dist)(state) state2 <- Turtle.turn(ToRight)(state1)} yield state2

Page 98: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

98//

(for { state1 <- sourceNew(Turtle.create("123", Position.zero, North)) state2 <- walkRight(1)(state1) state3 <- walkRight(1)(state2)} yield state3).events

Sourced monad

Page 99: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

99//

Problems we need to solve yet when composing commands:

- replaying previous events- accumulating new events- propagating new state

Problems left

Page 100: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

100//

Problems we need to solve yet when composing commands:

- replaying previous events- accumulating new events- propagating new state

Problems left

Page 101: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

101//

Sourced model demo

// Using for-comprehensionfor { state1 <- sourceNew[Turtle](Turtle.create("123", Position.zero, North)) state2 <- walkRight(1)(state1) state3 <- walkRight(1)(state2)} yield state3

Page 102: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

102//

Sourced model demo

// Using for-comprehensionfor { state1 <- sourceNew[Turtle](Turtle.create("123", Position.zero, North)) state2 <- walkRight(1)(state1) state3 <- walkRight(1)(state2)} yield state3

// Using flatMapsourceNew[Turtle](Turtle.create("123", Position.zero, North)) .flatMap(walkRight(1)) .flatMap(walkRight(1))

Page 103: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

103//

Problems we need to solve yet when composing commands:

- replaying previous events - accumulating new events- propagating state

Problems left

Page 104: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

104//

Spoiler:

There are even better ways to do it

(Kleisli anybody?)

Page 105: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

105//

04 // Further possibilities

Page 106: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

106//

Updating multiple instances

Page 107: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

107//

def together(turtle1: Turtle, turtle2: Turtle) (update: Turtle => Sourced[Turtle, TurtleEvent]) : Sourced[(Turtle, Turtle), TurtleEvent] = for { updated1 <- update(turtle1) updated2 <- update(turtle2) } yield (updated1, updated2)

Updating multiple aggregates

Page 108: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

108//

def together(turtle1: Turtle, turtle2: Turtle) (update: Turtle => Sourced[Turtle, TurtleEvent]) : Sourced[(Turtle, Turtle), TurtleEvent] = for { updated1 <- update(turtle1) updated2 <- update(turtle2) } yield (updated1, updated2)

// Caveat: consistency vs scalability - atomic persistence of events is only possible within a single shard/partition of the underlying store

Updating multiple aggregates

Page 109: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

109//

Handling concurrency

Page 110: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

110//

// So now we can write declarative programs which reify all the changes we want // to make to some state.

def myProgram(turtle: Turtle): Sourced[Turtle] = ( Sourced.pure(turtle) .flatMap(walkRight(1)) .flatMap(walkRight(1)) .flatMap(walk(2)))

Concurrency

Page 111: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

111//

// So now we can write declarative programs which reify all the changes we want // to make to some state.

def myProgram(turtle: Turtle): Sourced[Turtle] = ( Sourced.pure(turtle) .flatMap(walkRight(1)) .flatMap(walkRight(1)) .flatMap(walk(2)))

// It’s easy to introduce optimistic locking on top of it// and achieve something similar to STM

Concurrency

Page 112: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

112//

// Summing up

Page 113: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

113//

- Modeling and using events and handlers

- The limitation of an imperative approach

- How a functional approach can help us overcome these limitations

- Event sourcing can become an implementation detail

What we’ve seen today

Page 114: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

114//

Code examples are available at:https://github.com/eleaar/esmonad

Gimme some code

Page 115: EVENT SOURCING COMPOSABLE WITH MONADS€¦ · Composition in event sourcing Composing event handlers is easy - they’re just plain functions Composing commands is less trivial -

Merci.

115//

Daniel KRZYWICKI

[email protected]

@eleaar