Share
Embed
Rethinking "State Management"
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?
Join me to experience the difference perspective of State Management.
Tamizhvendan S
July 10, 2016
Transcript
166 slides
-
{ "tables" : [{"id" : 1, "status" : "available"}] }
{ "tables" : [{"id" : 1, "status" : "waiting_for_order"}] } { "tables" : [{"id" : 1, "status" : "in_service"}] } -
Closed Tab Placed Order Opened Tab Open Tab Place Order
Serve Drink Prepare Food -
Closed Tab Placed Order Opened Tab Open Tab Place Order
Serve Drink Prepare Food -
Closed Tab Placed Order Opened Tab Order InProgress Open Tab
Place Order Serve Drink Prepare Food -
Serve Drink Closed Tab Placed Order Opened Tab Order InProgress
Open Tab Place Order Serve Drink Prepare Food -
Serve Drink Prepare Food Closed Tab Placed Order Opened Tab
Order InProgress Open Tab Place Order Serve Drink Prepare Food -
Serve Drink Prepare Food Serve Food Closed Tab Placed Order
Opened Tab Order InProgress Open Tab Place Order Serve Drink Prepare Food -
Serve Drink Prepare Food Serve Food Closed Tab Placed Order
Opened Tab Order InProgress Open Tab Place Order Serve Drink Prepare Food -
Serve Drink Prepare Food Serve Food Closed Tab Placed Order
Opened Tab Order InProgress Open Tab Place Order Serve Drink Prepare Food -
Serve Drink Prepare Food Serve Food Closed Tab Placed Order
Opened Tab Order InProgress Served Order Open Tab Place Order Serve Drink Prepare Food -
Serve Drink Prepare Food Serve Food Closed Tab Placed Order
Opened Tab Order InProgress Served Order Open Tab Place Order Serve Drink Prepare Food -
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 -
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 -
State X State Y Command // State -> Command ->
State let evolve state command = let newState = // ... newState -
State X State Y Command // State -> Command ->
State let evolve state command = let newState = // ... newState -
State X State Y Command // State -> Command ->
State let evolve state command = let newState = // ... newState let state = ?? -
Closed Tab Opened Tab Open Tab initial state Tab Opened
event // State -> Command -> Event list let execute state command = let events = // ... events -
Closed Tab Placed Order Opened Tab Order InProgress Served Order
Tab Opened Order Placed -
Closed Tab Placed Order Opened Tab Order InProgress Served Order
Tab Opened Order Placed Drink Served Food Prepared -
Closed Tab Placed Order Opened Tab Order InProgress Served Order
Tab Opened Order Placed Drink Served Food Prepared Drink Served Food Prepared Food Served -
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 -
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 -
Events Are Facts Closed Tab Opened Tab Tab Opened event
current state new state -
Events Are Facts It’s like a Bank Statement! Closed Tab
Opened Tab Tab Opened event current state new state -
// State -> Event -> State let apply state event
= let newState = // ... newState Events Are Facts It’s like a Bank Statement! Closed Tab Opened Tab Tab Opened event current state new state -
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 -
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 -
Tab Opened 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 -
Tab Opened Order Placed 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 -
Tab Opened Order Placed Drink Served 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 -
Tab Opened Order Placed Drink Served Food Prepared 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 -
Tab Opened Order Placed Drink Served Food Prepared Food Served
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 -
Tab Opened Order Placed Drink Served Food Prepared Food Served
Order Served 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 -
Tab Opened Order Placed Drink Served Food Prepared Food Served
Order Served Tab Closed 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 -
Tab Opened Order Placed Drink Served Food Prepared Food Served
Order Served Tab Closed It’s append only! 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 -
past events Closed Tab initial state // State -> Event
-> State let apply state event = let newState = // ... newState Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed -
Closed Tab Tab Opened Opened Tab past events Closed Tab
initial state // State -> Event -> State let apply state event = let newState = // ... newState Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed -
Closed Tab Tab Opened Opened Tab past events Closed Tab
initial state Opened Tab Order Placed Placed Order // State -> Event -> State let apply state event = let newState = // ... newState Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed -
Closed Tab Tab Opened Opened Tab past events Closed Tab
initial state Opened Tab Order Placed Placed Order Placed Order Drink Served Order InProgress // State -> Event -> State let apply state event = let newState = // ... newState Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed -
Closed Tab Tab Opened Opened Tab past events Closed Tab
initial state Opened Tab Order Placed Placed Order Placed Order Drink Served Order InProgress Food Prepared Order InProgress Order InProgress // State -> Event -> State let apply state event = let newState = // ... newState Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed -
Closed Tab Tab Opened Opened Tab 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 // State -> Event -> State let apply state event = let newState = // ... newState Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed -
Closed Tab Tab Opened Opened Tab 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 // State -> Event -> State let apply state event = let newState = // ... newState Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed -
Closed Tab Tab Opened Opened Tab 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 -> State let apply state event = let newState = // ... newState Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed -
Closed Tab Tab Opened Opened Tab 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 Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed -
Closed Tab Tab Opened Opened Tab 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 ?? Tab Opened Order Placed Drink Served Food Prepared Food Served Order Served Tab Closed -
// State -> Command -> State let evolve state command
= let newState = // ... newState -
// 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 -
// 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 -
// 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 -
// 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 We started with this -
// 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 We started with this and evolved to this! -
// 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 Current state is a left-fold of past events Event Sourcing - Greg Young We started with this and evolved to this! -
// 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! -
// Event list -> Command -> Event list let evolve
= // ... Handling Commands -
// Event list -> Command -> Event list let evolve
= // ... // Command -> int let getTableId command = // ... // int -> Event list let getPastEvents tableId = // ... Handling Commands -
// 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 -
// 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 -
Serve Food Open Tab Place Order Serve Drink Prepare Food
Close Tab Commands -
{ "tables" : [{"id" : 1, "status" : "available"}] }
{ "tables" : [{"id" : 1, "status" : "waiting_for_order"}] } { "tables" : [{"id" : 1, "status" : "in_service"}] } Data Model -
{ "tables" : [{"id" : 1, "status" : "available"}] }
{ "tables" : [{"id" : 1, "status" : "waiting_for_order"}] } { "tables" : [{"id" : 1, "status" : "in_service"}] } Data Model Mutate during Commands -
{ "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 -
Event Projection Closed Tab Opened Tab Open Tab Tab Opened
Publish Message Queue Tab Opened Kafka, RabbitMQ, etc., -
Event Projection Closed Tab Opened Tab Open Tab Tab Opened
Publish Listen Message Queue Tab Opened Kafka, RabbitMQ, etc., -
Event Projection Closed Tab Opened Tab Open Tab Tab Opened
Publish ReadModel Projection Client Listen Message Queue Tab Opened Kafka, RabbitMQ, etc., -
Event Projection Closed Tab Opened Tab Open Tab Tab Opened
Publish ReadModel Projection Client Listen Populate Message Queue Tab Opened Kafka, RabbitMQ, etc., -
Event Projection Closed Tab Opened Tab Open Tab Tab Opened
Publish ReadModel Projection Client Listen Populate Message Queue Tab Opened Kafka, RabbitMQ, etc., -
Event Projection Closed Tab Opened Tab Open Tab Tab Opened
Publish ReadModel Projection Client Listen Populate Query API Server Message Queue Tab Opened Kafka, RabbitMQ, etc., -
Event Projection Closed Tab Opened Tab Open Tab Tab Opened
Publish ReadModel Projection Client Listen Populate Query API Server Read Message Queue Tab Opened Kafka, RabbitMQ, etc., -
Event Projection Closed Tab Opened Tab Open Tab Tab Opened
Publish ReadModel Projection Client Listen Populate Query API Server Read Pull Message Queue Tab Opened Kafka, RabbitMQ, etc., -
Event Projection Closed Tab Opened Tab Open Tab Tab Opened
Publish ReadModel Projection Client Listen Populate Query API Server Read Pull Message Queue Tab Opened Kafka, RabbitMQ, etc., -
Event Projection Closed Tab Opened Tab Open Tab Tab Opened
Publish ReadModel Projection Client Listen Populate Query API Server Read Listen Pull Message Queue Tab Opened Kafka, RabbitMQ, etc., -
Event Projection Closed Tab Opened Tab Open Tab Tab Opened
Publish ReadModel Projection Client Listen Populate Query API Server Read WebSocket Client Listen Pull Message Queue Tab Opened Kafka, RabbitMQ, etc., -
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 Message Queue Tab Opened Kafka, RabbitMQ, etc., -
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 Listen Message Queue Tab Opened Kafka, RabbitMQ, etc., -
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 Message Queue Tab Opened Kafka, RabbitMQ, etc., -
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 Message Queue Tab Opened Kafka, RabbitMQ, etc., -
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., -
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., -
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., -
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?) -
Read Model Projection { "tables" : [{"id" : 1, "status"
: "available"}] } -
Read Model Projection { "tables" : [{"id" : 1, "status"
: "available"}] } Tab Opened -
Read Model Projection { "tables" : [{"id" : 1, "status"
: "available"}] } Tab Opened { "tables" : [{"id" : 1, "status" : "waiting_for_order"}] } -
Read Model Projection { "tables" : [{"id" : 1, "status"
: "available"}] } Tab Opened { "tables" : [{"id" : 1, "status" : "waiting_for_order"}] } Order Placed -
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"}] } -
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 -
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 -
Error Handling User may post an invalid command Fetching Events
may fail Saving Events may fail -
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 -
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 -
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" // ... -
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 = // ... -
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 = // ... -
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 -
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 -
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 -
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/ -
Performance Tab Opened Order Placed Drink Served Food Prepared Food
Served Order Served Tab Closed -
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? -
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! -
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 -
Parallel Update Optimistic Locking Tab Opened 0 Order Placed 1
Drink Served 2 Food Prepared 3 Food Served 4 -
It’s not a “Silver Bullet” Thinks To Consider CQRS and
Event Sourcing are not top-level Architectures -
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 -
- ThoughtWorks Tech Radar 2012 F# is excellent at concisely
expressing business and domain logic - http://fsharp.org/testimonials/