Session: Event Sourcing Applicative Profunctors - Jeremy Chassaing

Event Sourcing in function programming

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.png

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.1.png

decide ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.2.png

pure function, immutable objects

evolve ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.3.png takes 1 event applies it to the state

Link it together ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.4.png

-–

initial state ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.5.png

-–

now we have Event Sourcing

command: only what is needed to ‘decide’ event: only what is needed to ’evolve'

cf Aggregate

Take any aggregate, and it has enough information to run, without needing external data -we can rewrite any aggregate to this -without needing external data

-– Decider

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.6.png decide: command -> event[] -> event[]

Decider composed of 6 things./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.7.png

-Command -Event -State (list of events, or denormalized) -decide : Cmd -> State -> Event[] -evolve: State -> Event -> State -initialState: State

-isTerminal: State -> bool # sometimes you want to end (suggested) eg “close the books, and start a new fiscal year”

-– Easy way

load state evolve state save state log / persist events -not used for continual state -used when big state changes (maintenance)

performance: only load&save every 10 events

-–

“progressive way of event sourcing”

no assumptions about - what command is - what event is - what decide is - what evolve is

-– Communicate outside aggregate boundary

example domain: todo-list

Bad pattern: ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.8.png state is ’todo list'

“I can’t use this, I need to save events I get from the outside”

Eventstore: events in exact order CosmoDB: partitions are not ordered -> need to save the order in which you noticed the events

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.9.png External events Ingest InternalEvents evolve state

if external source is ordered, no need to save ‘internal events’ if not ; you need to save internal events

React ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.10.png state + drift : Event

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.11.png if empty drift (drift is optional) returns all commands that need to happen (to recover from crash?)

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.12.png initial state + isTerminal (to check if state needs to be cleaned up)

Process ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.13.png

-–

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.14.png Actor –> Process

-–

Aggregate: inside needs to be consistent ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.15.png smaller boundary = better!

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.16.png outside does not have to be consistent, but can be

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.17.png ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.18.png we can split it whenever we want but we can combine them for now (as long as we don’t make connections between them that make it impossible to split them later)

-–

Example: Game Uno ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.19.png Game Hand 1 Hand 2 Hand 3 Stockpile

V1, everything async would be overkill ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.20.png

-–

Functor you can have a select or map operation so that if you have a function A:string -> B:int list of strings -> list of int

-–

Co-variant functor function A -> B F -> F

select / map : (f : a -> b) (List) = List ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.21.png

c -> (state -> eventlist) is incompatible with c-> d

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.22.png ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.23.png decide f

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.24.png compose a function that takes d

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.25.png Game (GameCommand) StockPile (stockpileCommand) Hands ()

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.26.png -> some ?? -> GameDecider -> some ?? -> StockpileDecider

or if none -> empty list

-–

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.27.png GameEvent StockpileEvent …

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.28.png -> some -> evolve Event + State -> None

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.29.png GameDecicer -> GameEvent -> GameEvent StockpileDecicer -> StockpileEvent -> StockpileEvent ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.30.png

-–

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.31.png Commandx, Eventx, ?x, dx, ec, initialStatex, isTerminalx

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.32.png

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.33.png

Make a pair of both states ./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.34.png

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.35.png Cx -> DecideX Cx StateX |> map Eventx Cy -> Decide of Cy StateY |> map EventY

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.36.png evolve: E S -> match E with | Event for x -> evolve EventForX StateX EventForX |> {s with x = evolve EventForX StateX }

-–

./resources/session-event-sourcing-applicative-profunctors-jer.resources/screenshot.37.png


> > at this point I dropped off, next session was starting >