event sourcing - sdd conference › brands › sdd › library › event_sourcing.pdf · simple...

30
EVENT SOURCING MICHAEL NEWTON @MAVNN LTD

Upload: others

Post on 23-Jun-2020

6 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

EVENT SOURCINGMICHAEL NEWTON

@MAVNN LTD

Page 2: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

MONOIDSErm... what?

Page 3: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

SIMPLE RULESTake an operation (Op)Take a type ('T)Op : 'T -> 'T -> 'T (Closure)Op t1 (Op t2 t3) = Op (Op t1 t2) t3(Associativity)There is a t0 such that: Op t0 t1 = Op t1 t0 (Zero)

Page 4: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

EXAMPLE 1 - INTEGER ADDITION1 + (2 + 3) = (1 + 2) + 3

1 + 0 = 0 + 1

Page 5: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

EXAMPLE 2 - STRING CONCATINATION("Bob" + "Fred") + "Smith" = "Bob" + ("Fred" + "Smith")

"Bob" + "" = "" + "Bob"

Page 6: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

POP QUIZ 1 - EMAIL SUCCESS COUNT1: 2: 3: 4: 5: 6: 7:

type EmailsSent = { SuccessCount : int FailCount : int }

let addEmailsSent es es' = { SuccessCount = es.SuccessCount + es'.SuccessCount FailCount = es.FailCount + es'.FailCount }

Monoid? Yes or no?

Page 7: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

POP QUIZ 2 - MEANS1: 2: let mean (x : int) (y : int) = (float x + float y) / 2.0

Monoid? Yes or no?

Page 8: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

FAIL!

Page 9: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

MEANSNo closure: int -> int -> floatNot associative: mean 1 (mean 2 3) <> mean (mean 1 2) 3... even if you ignore the type error

Page 10: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

BUT...1: 2: 3: 4: 5: 6: 7:

type MeanTracker = { Total : int Divisor : int }

let addMean mt mt' = { Total = mt.Total + mt'.Total Divisor = mt.Divisor + mt'.Divisor }

Page 11: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

THAT'S GREAT......but why are we doing this?

BECAUSE LISTS.(or IEnumerable<'T>, if you're that way inclined)

Page 12: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

REDUCEOF MAP/REDUCE FAME

1: 2: 3: 4: 5: 6: 7: 8: 9: 10:

let currentMean = [{ Total = 10; Divisor = 1 } { Total = 20; Divisor = 1 }] |> List.reduce addMean

// Some more data comes in... let newMean = [currentMean { Total = 15; Divisor = 1}] |> List.reduce addMean

Incremental and parallel processing are trivial

Page 13: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

THIS COMPILES...1: 2: 3:

// No data yet... [] |> List.reduce addMean

Page 14: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

PARTIAL FUNCTIONS ARE EVILA partial function is one that can't create a valid output forevery "valid" inputReduce is a partial function as it can't operate on emptylists

Page 15: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

LET'S TURN IT UP TO ELEVEN

FOLD1: 2: 3:

// Still no data [] |> List.fold addMean { Total = 0; Divisor = 0 }

Page 16: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

SUMMARY SO FARIf you have, or you can make a monoid:

You can always reduce lists down to a single summaryvalueYou can incrementally process the listFor (lots) more on monoids, read the series by ScottWlaschin

Page 17: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

EVENT SOURCING?Nearly... but �rst!

Page 18: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

GENERALISING FOLDaddMean had type: MeanTracker -> MeanTracker -> MeanTrackerBut the �rst parameter to fold has signature: 'State ->'T -> 'State

Page 19: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

EXAMPLE "PROJECTION" FOLD1: 2: 3:

// Not a monoid operator - no closure let emailsSent total es = total + es.SuccessCount

Page 20: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

INCREMENTAL SEND TOTALS! 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:

let currentOutput = [{ SuccessCount = 10; FailCount = 0 } { SuccessCount = 22; FailCount = 1 }] |> List.fold emailsSent 0

// More emails get sent let newCurrentOutput = [{ SuccessCount = 112; FailCount = 2 } { SuccessCount = 100; FailCount = 5 }] |> List.fold emailsSent currentOutput

Page 21: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

And then...

...the customer tells you they want to know the percentagefailure rate of sends. Preferably with historic data. And, of

course, live updates.

Page 22: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

NO PROBLEM!1: 2: 3: 4:

// Remember our MeanTracker object?let averageEmailSuccess mt es = { Total = mt.Total + es.SuccessCount Divisor = mt.Divisor + (es.SuccessCount + es.FailCount) }

Page 23: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

INCREMENTAL AVERAGE SUCCESS RATES! 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:

// We already have this data let currentRate = [{ SuccessCount = 10; FailCount = 0 } { SuccessCount = 22; FailCount = 1 } { SuccessCount = 112; FailCount = 2 } { SuccessCount = 100; FailCount = 5 }] |> List.fold averageEmailSuccess { SuccessCount = 0; FailCount = 0 }

// Now more starts coming in let newCurrentRate = [{ SuccessCount = 15; FailCount = 4 } { SuccessCount = 30; FailCount = 0 }] |> List.fold averageEmailSuccess currentRate

Page 24: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

EVENT SOURCINGFinally...

Page 25: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

EVENT SOURCING BASICSStore the domain events as "lists" (normally called streams)Build projections of them using folds

Page 26: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

DOMAIN EVENTSA thing that has already happenedEmailSent; InvoicePaidDomain events can't fail - they've already happened!Compare with command: SendEmail; PayInvoice

Page 27: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

SIMPLE INTERFACEno object relational impedance mismatch

Page 28: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

SIMPLE INTERFACE1: 2: 3: 4: 5: 6: 7: 8: 9:

open System

type IRepository<'state, 'event> = abstract member LoadAggregate<'state> : Guid -> Async<'state * int> abstract member RefreshAggregate<'state> : Guid -> int -> 'state -> Async<'state * abstract member AppendEvents<'event> : Guid -> int -> 'event seq -> Async<int>

Page 29: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

LET'S SEE AN EXAMPLE IN ACTION...

Page 30: EVENT SOURCING - SDD Conference › brands › sdd › library › Event_Sourcing.pdf · SIMPLE RULES Take an operation (Op) Take a type ('T) Op : 'T -> 'T -> 'T (Closure) Op t1 (Op

QUESTIONS?@mavnn ltd

blog

qvitoo