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.

C6dbbb399f7658917f60bd1e2a2663ed?s=128

Tamizhvendan S

August 26, 2016
Tweet

Transcript

  1. Rethinking State Management currentState = foldl(pastEvents, initialState)

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

    bit.ly/fsapplied
  3. Rethinking ? State ??

  4. CafeApp Courtesy: http://cqrs.nu/

  5. Old School Backend Data Store Imperative Data Modeling!

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

    { "tables" : [{"id" : 1, "status" : "waiting_for_order"}] } { "tables" : [{"id" : 1, "status" : "in_service"}] }
  7. A Different Perspective

  8. t0 t1 Open Tab t2 Place Order

  9. t0 {"status" : "available"} t1 {"status" : "waiting_for_order"} t2 {"status"

    : "in_service"}
  10. STATE is Transient

  11. 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
  12. State X State Y Command // State -> Command ->

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

    event // State -> Command -> Event list let execute state command = let events = // ... events
  14. 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
  15. // 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
  16. 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!
  17. 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 ??
  18. // 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
  19. // 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!
  20. // 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
  21. // 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
  22. Serve Food Open Tab Place Order Serve Drink Prepare Food

    Close Tab Commands
  23. Table Status Food(s) To Prepare Item(s) To Serve Amount To

    Pay Queries!!
  24. Old School Backend Data Store Imperative Data Modeling!

  25. { "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
  26. Single Responsibility Commands Queries Command Query Responsibility Separation

  27. 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?)
  28. 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
  29. 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
  30. 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 = // ...
  31. 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/
  32. 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
  33. Parallel Update Optimistic Locking Tab Opened 0 Order Placed 1

    Drink Served 2 Food Prepared 3 Food Served 4
  34. 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
  35. - ThoughtWorks Tech Radar 2012 https://github.com/tamizhvendan/CafeApp bit.ly/fsapplied

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