Slide 1

Slide 1 text

Rethinking State Management currentState = foldl(pastEvents, initialState)

Slide 2

Slide 2 text

About Me Pragmatic, Passionate and Polyglot Programmer @tamizhvendan http://blog.tamizhvendan.in www.ajira.tech bit.ly/fsapplied

Slide 3

Slide 3 text

Rethinking ? State ??

Slide 4

Slide 4 text

CafeApp Courtesy: http://cqrs.nu/

Slide 5

Slide 5 text

Old School Backend Data Store Imperative Data Modeling!

Slide 6

Slide 6 text

{ "tables" : [{"id" : 1, "status" : "available"}] } { "tables" : [{"id" : 1, "status" : "waiting_for_order"}] } { "tables" : [{"id" : 1, "status" : "in_service"}] }

Slide 7

Slide 7 text

A Different Perspective

Slide 8

Slide 8 text

t0 t1 Open Tab t2 Place Order

Slide 9

Slide 9 text

t0 {"status" : "available"} t1 {"status" : "waiting_for_order"} t2 {"status" : "in_service"}

Slide 10

Slide 10 text

STATE is Transient

Slide 11

Slide 11 text

Serve Drink Prepare Food Serve Food Closed Tab Placed Order Opened Tab Order InProgress Served Order Open Tab Place Order Serve Drink Prepare Food Close Tab

Slide 12

Slide 12 text

State X State Y Command // State -> Command -> State let evolve state command = let newState = // ... newState

Slide 13

Slide 13 text

Closed Tab Opened Tab Open Tab initial state Tab Opened event // State -> Command -> Event list let execute state command = let events = // ... events

Slide 14

Slide 14 text

Closed Tab Placed Order Opened Tab Order InProgress Served Order Tab Opened Order Placed Drink Served Food Prepared Drink Served Food Prepared Food Served Tab Closed Order Served

Slide 15

Slide 15 text

// State -> Event -> State let apply state event = let newState = // ... newState Events Are Facts Yes, It’s like a Bank Statement! Closed Tab Opened Tab Tab Opened event current state new state

Slide 16

Slide 16 text

Closed Tab Placed Order Opened Tab Order InProgress Served Order Tab Opened Order Placed Drink Served Food Prepared Drink Served Food Prepared Food Served Order Served Tab Closed Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed It’s append only!

Slide 17

Slide 17 text

Closed Tab Tab Opened Opened Tab Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed past events Closed Tab initial state Opened Tab Order Placed Placed Order Placed Order Drink Served Order InProgress Food Prepared Order InProgress Order InProgress Food Served Order InProgress Order InProgress Order Served Served Order Order InProgress Tab Closed Closed Tab Served Order // State -> Event list -> State let computeState initialState pastEvents = List.fold apply initialState pastEvents // State -> Event -> State let apply state event = let newState = // ... newState Performance ??

Slide 18

Slide 18 text

// State -> Command -> State let evolve state command = let newState = // ... newState // State -> Command -> Event list let execute state command = let events = // ... events // State -> Event list -> State let computeState initialState pastEvents = List.fold apply initialState pastEvents // State -> Event -> State let apply state event = let newState = // ... newState // Event list -> Command -> Event list let evolve pastEvents command = let state = computeState TabClosed pastEvents execute state command

Slide 19

Slide 19 text

// State -> Command -> State let evolve state command = let newState = // ... newState // Event list -> Command -> Event list let evolve pastEvents command = let state = computeState TabClosed pastEvents execute state command currentState = foldl(pastEvents, initialState) Current state is a left-fold of past events Event Sourcing - Greg Young We started with this and evolved to this!

Slide 20

Slide 20 text

// Event list -> Command -> Event list let evolve = // ... // Command -> int let getTableId command = // ... // int -> Event list let getPastEvents tableId = // ... // Command -> Event list let handleCommand command = let pastEvents = getTableId command |> getPastEvents evolve pastEvents command Handling Commands

Slide 21

Slide 21 text

// Command -> Event list let handleCommand command = let pastEvents = getTableId command |> getPastEvents evolve pastEvents command // HttpRequest -> HttpResponse let commandHandler httpRequest = let command = // get Command from HTTP request let newEvents = handleCommand command saveEvents newEvents // return OK response Handling Commands

Slide 22

Slide 22 text

Serve Food Open Tab Place Order Serve Drink Prepare Food Close Tab Commands

Slide 23

Slide 23 text

Table Status Food(s) To Prepare Item(s) To Serve Amount To Pay Queries!!

Slide 24

Slide 24 text

Old School Backend Data Store Imperative Data Modeling!

Slide 25

Slide 25 text

{ "tables" : [{"id" : 1, "status" : "available"}] } { "tables" : [{"id" : 1, "status" : "waiting_for_order"}] } { "tables" : [{"id" : 1, "status" : "in_service"}] } Data Model Mutate during Commands Fetch during Queries

Slide 26

Slide 26 text

Single Responsibility Commands Queries Command Query Responsibility Separation

Slide 27

Slide 27 text

Event Projection Closed Tab Opened Tab Open Tab Tab Opened Publish ReadModel Projection Client Listen Populate Query API Server Read WebSocket Client Listen Push Pull Data Analytics Client Listen Populate Message Queue Tab Opened Kafka, RabbitMQ, etc., Uni directional Data flow (Flux?)

Slide 28

Slide 28 text

Read Model Projection { "tables" : [{"id" : 1, "status" : "available"}] } Tab Opened { "tables" : [{"id" : 1, "status" : "waiting_for_order"}] } Order Placed { "tables" : [{"id" : 1, "status" : "in_service"}] } Order Served

Slide 29

Slide 29 text

Error Handling User may post an invalid command Fetching Events may fail Saving Events may fail type Result<'T,'E> = | Success of 'T | Error of 'E

Slide 30

Slide 30 text

User may post an invalid command // Event list -> Command -> Event list let evolve = // ... // Command -> Event list let handleCommand command = let pastEvents = getTableId command |> getPastEvents evolve pastEvents command // State -> Command -> Event list let execute state command = let events = // ... events // State -> Command -> Result let execute state command = match command with | OpenTab -> match state with | ClosedTab -> Success [TabOpened] | _ -> Error "Tab already opened" // ... // Event list -> Command -> Result let evolve = // ... // Command -> Result let handleCommand command = // ...

Slide 31

Slide 31 text

User may post an invalid command // HttpRequest -> HttpResponse let commandHandler httpRequest = let command = // get Command from HTTP request let newEvents = handleCommand command saveEvents newEvents // return OK response // HttpRequest -> HttpResponse let commandHandler httpRequest = let command = //... match handleCommand command with | Success newEvents -> saveEvents newEvents // return OK response | Error message -> // return BAD_REQUEST Fetching Events may fail Saving Events may fail https://fsharpforfunandprofit.com/rop/

Slide 32

Slide 32 text

Performance Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed Do I need to fetch all the events to compute the state every time? Need not to be! Snapshots

Slide 33

Slide 33 text

Parallel Update Optimistic Locking Tab Opened 0 Order Placed 1 Drink Served 2 Food Prepared 3 Food Served 4

Slide 34

Slide 34 text

It’s not a “Silver Bullet” Thinks To Consider A whole system based on Event Sourcing is an Anti-Pattern CQRS and Event Sourcing are not top-level Architectures

Slide 35

Slide 35 text

- ThoughtWorks Tech Radar 2012 https://github.com/tamizhvendan/CafeApp bit.ly/fsapplied

Slide 36

Slide 36 text

https://www.meetup.com/Chennai-F-User-Group/events/233229191/