event sourcing - sddconf.comsddconf.com/brands/sdd/library/event_sourcing.pdf · event sourcing...
TRANSCRIPT
![Page 2: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/2.jpg)
MONOIDSErm... what?
![Page 3: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/3.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/4.jpg)
EXAMPLE 1 - INTEGER ADDITION1 + (2 + 3) = (1 + 2) + 3
1 + 0 = 0 + 1
![Page 5: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/5.jpg)
EXAMPLE 2 - STRING CONCATINATION("Bob" + "Fred") + "Smith" = "Bob" + ("Fred" + "Smith")
"Bob" + "" = "" + "Bob"
![Page 6: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/6.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/7.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/8.jpg)
FAIL!
![Page 9: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/9.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/10.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/11.jpg)
THAT'S GREAT......but why are we doing this?
BECAUSE LISTS.(or IEnumerable<'T>, if you're that way inclined)
![Page 12: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/12.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/13.jpg)
THIS COMPILES...1: 2: 3:
// No data yet... [] |> List.reduce addMean
![Page 14: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/14.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/15.jpg)
LET'S TURN IT UP TO ELEVEN
FOLD1: 2: 3:
// Still no data [] |> List.fold addMean { Total = 0; Divisor = 0 }
![Page 16: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/16.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/17.jpg)
EVENT SOURCING?Nearly... but �rst!
![Page 18: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/18.jpg)
GENERALISING FOLDaddMean had type: MeanTracker -> MeanTracker -> MeanTrackerBut the �rst parameter to fold has signature: 'State ->'T -> 'State
![Page 19: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/19.jpg)
EXAMPLE "PROJECTION" FOLD1: 2: 3:
// Not a monoid operator - no closure let emailsSent total es = total + es.SuccessCount
![Page 20: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/20.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/21.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/22.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/23.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/24.jpg)
EVENT SOURCINGFinally...
![Page 25: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/25.jpg)
EVENT SOURCING BASICSStore the domain events as "lists" (normally called streams)Build projections of them using folds
![Page 26: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/26.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/27.jpg)
SIMPLE INTERFACEno object relational impedance mismatch
![Page 28: EVENT SOURCING - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/28.jpg)
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 - sddconf.comsddconf.com/brands/sdd/library/Event_Sourcing.pdf · EVENT SOURCING Finally... EVENT SOURCING BASICS Store the domain events as "lists" (normally called](https://reader035.vdocuments.us/reader035/viewer/2022070217/611e9d81eb13e80362093fa7/html5/thumbnails/29.jpg)
LET'S SEE AN EXAMPLE IN ACTION...