Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rethinking "State Management" - TW Geeknight

Rethinking "State Management" - TW Geeknight

Persisting the State is an integral part of any application, and it profoundly influences how we architect the application. But do we need to store the state in the first place? Is there any alternative?

You will learn:

* Functional-First Programming with F#
* A different view on how to manage state
* A good understanding of Event Sourcing and CQRS principles

Requirements:

* No prior knowledge of F# or Event Sourcing is required
* You'll need a Mac, Windows or Linux laptop with F# installed. See www.fsharp.org for installation instructions.

Together, let's experience a difference perspective of State Management.

Tamizhvendan S

August 26, 2016
Tweet

More Decks by Tamizhvendan S

Other Decks in Programming

Transcript

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

    { "tables" : [{"id" : 1, "status" : "waiting_for_order"}] } { "tables" : [{"id" : 1, "status" : "in_service"}] }
  2. 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
  3. State X State Y Command // State -> Command ->

    State let evolve state command = let newState = // ... newState
  4. Closed Tab Opened Tab Open Tab initial state Tab Opened

    event // State -> Command -> Event list let execute state command = let events = // ... events
  5. 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
  6. // 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
  7. 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!
  8. 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 ??
  9. // 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
  10. // 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!
  11. // 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
  12. // 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
  13. { "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
  14. 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?)
  15. 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
  16. 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
  17. 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<Event list,string> let execute state command = match command with | OpenTab -> match state with | ClosedTab -> Success [TabOpened] | _ -> Error "Tab already opened" // ... // Event list -> Command -> Result<Event list, string> let evolve = // ... // Command -> Result<Event list, string> let handleCommand command = // ...
  18. 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/
  19. 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
  20. Parallel Update Optimistic Locking Tab Opened 0 Order Placed 1

    Drink Served 2 Food Prepared 3 Food Served 4
  21. 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