SlideShare a Scribd company logo
Event Sourcing in the
Functional World
A practical journey of using F#
and event sourcing
● Why Event Sourcing?
● Domain Design: Event Storming
● Primitives, Events, Commands, and
Aggregates
● Domain Logic as Pure Functions
● Serialization and DTOs
● Error Handling: Railway-Oriented
Programming
● Cosmos DB: Event / Aggregate Store
● Strong(-er) Consistency at Scale
● Assembling the Pipeline: Dependency
Rejection
● Change Feed: Eventually Consistent Read
Models https://fsharpforfunandprofit.com/
Why Event
Sourcing?
• We need a reliable audit log
• We need to know the the
state of the system at a
given point in time
• We believe it helps us to build
more scalable systems*
(your mileage may vary)
“Event Sourcing -
Step by step in
F#”
https://medium.com/@dzoukr/event
-sourcing-step-by-step-in-f-
be808aa0ca18
“Scaling Event-
Sourcing at Jet”
https://medium.com/@eulerfx/scalin
g-event-sourcing-at-jet-
9c873cac33b8
Event
Storming
Form a shared mental
model, avoid
technical jargon
Focus on business
events, not data
structures
Discover Events,
Commands, and
Domain Aggregates
Primitives
The modelling
building blocks
Simple Wrappers
type FirstName = FirstName of string
type LastName = LastName of string
type MiddleName = MiddleName of string
Wrappers with guarded access (recommended)
type EmailAddress = private EmailAddress of string
[<RequireQualifiedAccess>]
module EmailAddress =
let create email =
if Regex.IsMatch(email, ".*?@(.*)")
then email |> EmailAddress |> Ok
else Error "Incorrect email format"
let value (EmailAddress e) = e
Composition
type ContactDetails = { Name: Name; Email: EmailAddress }
and Name =
{ FirstName: FirstName
MiddleName: MiddleName option
LastName: LastName }
Events
What happened
to the system?
Different event types are just DU cases
type VaccinationEvent =
| ContactRegistered of ContactDetails
| AppointmentCreated of VaccinationAppointment
| AppointmentCanceled of AppointmentId
| VaccineAdministered of AppointmentId
| ObservationComplete of AppointmentId * ObservationStatus
| SurveySubmitted of AppointmentId * SurveyResult
Metadata:
● Timestamp
● Aggregate Id
● Sequence No.
● Correlation Id
● Causation Id
● Grouping
attributes
and VaccinationAppointment =
{ Id: AppointmentId
Vaccine: VaccineType
Hub: VaccinationHub
Date: DateTime }
and AppointmentId = string
and VaccineType = Pfizer | Moderna | AstraZeneca
and ObservationStatus =
| NoAdverseReaction
| AdverseReaction of ReactionKind
and VaccinationHub = exn
and ReactionKind = exn
and SurveyResult = exn
type Event<'D, 'M> = { Id: Guid; Data: 'D; Metadata: 'M }
What caused
the change?
(Sometimes)
Who and why
requested the
change?
Commands Commands are intents, and reflect actions.
Naming: usually “VerbNoun”
type VaccinationCommand =
| RegisterContact of ContactDetails
| CreateAppointment of VaccinationAppointment
| CancelAppointment of AppointmentId
| AdministerVaccine of AppointmentId
* ObservationStatus option
| SubmitSurvey of AppointmentId * SurveyResult
Valid commands generate events
let toEvents c = function
| RegisterContact cd -> [ ContactRegistered cd ]
| CreateAppointment a -> [ AppointmentCreated a ]
| CancelAppointment appId -> [ AppointmentCanceled appId ]
| AdministerVaccine (appId, None) ->
[ VaccineAdministered appId ]
| AdministerVaccine (appId, Some status) ->
[ VaccineAdministered appId
ObservationComplete (appId, status) ]
| SubmitSurvey (appId, s) -> [ SurveySubmitted (appId, s) ]
Event.Metadata.CausationId = Command.Id
“Designing with
types: Making
illegal states
unrepresentable”
https://fsharpforfunandprofit.com/po
sts/designing-with-types-making-
illegal-states-unrepresentable/
Domain
Aggregates
Aggregates should have enough information to power
business rules
type VaccineRecipient =
{ Id: Guid
ContactDetails: ContactDetails
RegistrationDate: DateTime
State: VaccineRecipientState }
and VaccineRecipientState =
| Registered
| Booked of VaccinationAppointment nlist
| InProcess of VaccinationInProcess
| FullyVaccinated of VaccinationResult nlist
and VaccinationInProcess =
{ Administered: VaccinationResult nlist
Booked: VaccinationAppointment nlist }
and VaccinationResult = VaccinationAppointment
* ObservationStatus option
* SurveyResult option
and 'T nlist = NonEmptyList<'T>
and NonEmptyList<'T> = 'T list
Pure functions
Domain
Logic
Event sourcing in a nutshell
type Folder<'Aggregate, 'Event> =
'Aggregate -> 'Event list -> 'Aggregate
type Handler<'Aggregate, 'Command, 'Event> =
'Aggregate -> 'Command -> 'Aggregate * 'Event list
Add error handling, separate “create” and “update”
let create newId timestamp event =
match event with
| ContactRegistered c ->
{ Id = newId; ContactDetails = c;
RegistrationDate = timestamp; State = Registered } |> Ok
| _ -> Error "Aggregate doesn't exist"
let update aggregate event =
match aggregate.State, event with
| Registered, AppointmentCreated apt ->
{ aggregate with State = Booked [ apt ] } |> Ok
| Booked list, AppointmentCreated apt ->
{ aggregate with State = Booked (list @ [apt] ) } |> Ok
| _, _ -> "Exercise left to the reader" |> Error
“Serializing your
domain model”
https://fsharpforfunandprofit.
com/posts/serializing-your-
domain-model/
DTOs
DTOs reflect unvalidated input outside of our control
type NameDto =
{ FirstName: string
MiddleName: string // can be null :(
LastName: string }
module NameDto =
let toDomain (name: NameDto) =
({ FirstName = name.FirstName |> FirstName
MiddleName = name.MiddleName
|> Option.ofObj |> Option.map MiddleName
LastName = name.LastName |> LastName }: Name) |> Ok
You might need to apply versioning to your DTOs
type VaccinationEventDto =
| AppointmentCreated of VaccinationAppointment
| AppointmentCreatedV2 of VaccinationAppointment
* Confirmation
Events are stored forever, design them carefully!
Error
Handling
Error Handling is tedious
type ContactDetailsDto = { Name: NameDto; Email: string }
module ContactDetailsDto =
let toDomainTheBoringWay contact =
let nameResult = contact.Name |> NameDto.toDomain
match (nameResult) with
| Ok name ->
let emailResult = contact.Email |> EmailAddress.create
match emailResult with
| Ok email ->
({ Name = name; Email = email }: ContactDetails) |> Ok
| Error e -> Error e
| Error e -> Error e
“Railway Oriented
Programming”
https://fsharpforfunand
profit.com/rop/
FsToolkit.ErrorHandling helps
module ContactDetailsDto =
let toDomain contact = result {
let! name = contact.Name |> NameDto.toDomain
let! email = contact.Email |> EmailAddress.create
return ({ Name = name; Email = email }: ContactDetails)
}
There and back
again
Libraries:
FSharp.Json
https://github.com/vsaprono
v/FSharp.Json
Thoth.Json (works
with Fable!)
https://thoth-
org.github.io/Thoth.Json/
Serialization FSharp.Json can handle wrapper types and options
let record: Name =
{ FirstName = FirstName "John"
MiddleName = None
LastName = LastName "Smith" }
record |> Json.serialize |> printfn "%s"
{
"FirstName": "John",
"MiddleName": null,
"LastName": "Smith"
}
JSON -> DTO can fail
let tryParse<'Data> s =
try
Json.deserialize<'Data> s |> Ok
with
| x -> sprintf "Cannot deserialize: %s" x.Message |> Error
JSON -> Domain? Compose using ROP
let readContacts contactsString =
contactsString
|> tryParse<ContactDetailsDto>
|> Result.bind ContactDetailsDto.toDomain
CosmosDB:
Event /
Aggregate Store
Persistence
Define partition strategy in the code, not DB
let! response =
database.CreateContainerIfNotExistsAsync(
containerName, "/partitionKey")
module VaccinationRecipientKeyStrategy =
let toPartitionKey streamId =
$"VaccinationRecipient-{streamId}"
Partition boundaries determine consistency
Stream 1
(partition 1)
Event 1 Event 2 Event 3
Aggregate
Stream 2
(partition 2)
Event 1 Event 2
Aggregate
Defining Entities
in F#
Persistence Useful DB Wrappers
module Db =
type PartitionKey = PartitionKey of string
type Id = Id of string
type ETag = ETag of string
type EntityType = Event | AggregateRoot
Use JSON serializer specifics to map to DB fields
type PersistentEntity<'Payload> =
{ [<JsonField("partitionKey")>]
PartitionKey: Db.PartitionKey
[<JsonField("id")>] Id: Db.Id
[<JsonField("etag")>] ETag: Db.ETag option
Type: EntityType
Payload: 'Payload }
Concrete serialized types
module VaccinationPersistence =
type Event = PersistentEntity<VaccinationEvent>
type Aggregate = PersistentEntity<VaccineRecipient>
CosmosDB
Transactions
Persistence Use Batch API to add events + “upsert” the aggregate
module CosmosDb =
let createBatch (Db.PartitionKey key) (container: Container) =
container.CreateTransactionalBatch(PartitionKey(key))
let createAsString (payload: string)
(batch: TransactionalBatch) =
new MemoryStream(Encoding.UTF8.GetBytes(payload))
|> batch.CreateItemStream
let replaceAsString (Db.Id id) (payload: string)
(Db.ETag etag)
(batch: TransactionalBatch) =
let stream = new MemoryStream
(Encoding.UTF8.GetBytes(payload))
let options = TransactionalBatchItemRequestOptions()
options.IfMatchEtag <- etag
batch.ReplaceItemStream(id, stream, options)
let executeBatch (batch: TransactionalBatch) = taskResult {
let! response = batch.ExecuteAsync()
if (response.IsSuccessStatusCode) then
return! Ok ()
else
return! Error (response.StatusCode, response.ErrorMessage)
}
We use tasks for an easier
interop with Azure SDKs
Optimistic
concurrency
“Dependency
Rejection”
https://blog.ploeh.dk/2017/01/
27/from-dependency-
injection-to-dependency-
rejection/
“Reinventing the
Transaction
Script”
https://fsharpforfunandprofit.
com/transactionscript/
Assembling
the Pipeline
The Impure - Pure - Impure “sandwich”
• Read data
• Deserialize DTOs from JSON
• Convert DTOs to Domain Models
• Generate commands
• Run the handler
• Convert Results to DTOs
• Serialize DTOs to JSON
• Persist the output
Do not do I/O in pure code, return directives instead
module VaccinationExample =
type ErrorMessage = string
type DomainEvent = Event<VaccinationEvent, EventMetadata>
type HandlerResult =
| NoChange
| InvalidCommand of ErrorMessage
| Conflict of ErrorMessage
| CreateAggregate of VaccineRecipient * DomainEvent list
| UpdateAggregate of VaccineRecipient * DomainEvent list
type Handler = VaccineRecipient option
-> VaccinationCommand -> HandlerResult
Using
CosmosDB to
build read
models
Change
Feed
You can use either:
• Azure Functions CosmosDBTrigger (simpler)
• or ChangeFeed Processor running in AppServices
(more control)
You will need to provision:
• A separate Lease collection in CosmosDB
• The target storage (DB, blobs, SQL DB, etc)
Leases ensure events will be processed in sync, but...
Events might come in several partitions at once!
let groups = docs
|> Seq.groupBy
(fun d ->
d.GetPropertyValue<string> "partitionKey")
for (pk, group) in groups do // process by partitions
You have to handle errors (make your own DLQ, etc)
You can reprocess events from the beginning!
Questions?
Thank you
flarehr.com.au

More Related Content

What's hot

JavaScript - Chapter 9 - TypeConversion and Regular Expressions
 JavaScript - Chapter 9 - TypeConversion and Regular Expressions  JavaScript - Chapter 9 - TypeConversion and Regular Expressions
JavaScript - Chapter 9 - TypeConversion and Regular Expressions WebStackAcademy
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaWiem Zine Elabidine
 
MongoDB Aggregation
MongoDB Aggregation MongoDB Aggregation
MongoDB Aggregation Amit Ghosh
 
Morel, a Functional Query Language
Morel, a Functional Query LanguageMorel, a Functional Query Language
Morel, a Functional Query LanguageJulian Hyde
 
The Power of Composition (NDC Oslo 2020)
The Power of Composition (NDC Oslo 2020)The Power of Composition (NDC Oslo 2020)
The Power of Composition (NDC Oslo 2020)Scott Wlaschin
 
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional ProgrammingZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional ProgrammingJohn De Goes
 
Introduction to Functional Programming with Scala
Introduction to Functional Programming with ScalaIntroduction to Functional Programming with Scala
Introduction to Functional Programming with Scalapramode_ce
 
Why functional programming and category theory strongly matters
Why functional programming and category theory strongly mattersWhy functional programming and category theory strongly matters
Why functional programming and category theory strongly mattersPiotr Paradziński
 
Prototype programming in JavaScript
Prototype programming in JavaScriptPrototype programming in JavaScript
Prototype programming in JavaScriptTimur Shemsedinov
 
JavaScript - Chapter 15 - Debugging Techniques
 JavaScript - Chapter 15 - Debugging Techniques JavaScript - Chapter 15 - Debugging Techniques
JavaScript - Chapter 15 - Debugging TechniquesWebStackAcademy
 
PHP Cookies and Sessions
PHP Cookies and SessionsPHP Cookies and Sessions
PHP Cookies and SessionsNisa Soomro
 
Multiple inheritance in c++
Multiple inheritance in c++Multiple inheritance in c++
Multiple inheritance in c++Sujan Mia
 
Node.js middleware: Never again!
Node.js middleware: Never again!Node.js middleware: Never again!
Node.js middleware: Never again!Timur Shemsedinov
 
Functional Principles for OO Developers
Functional Principles for OO DevelopersFunctional Principles for OO Developers
Functional Principles for OO Developersjessitron
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldFunctional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldJorge Vásquez
 
Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)Scott Wlaschin
 
Object Calisthenics Applied to PHP
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHPGuilherme Blanco
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and EffectsMartin Odersky
 

What's hot (20)

JavaScript - Chapter 9 - TypeConversion and Regular Expressions
 JavaScript - Chapter 9 - TypeConversion and Regular Expressions  JavaScript - Chapter 9 - TypeConversion and Regular Expressions
JavaScript - Chapter 9 - TypeConversion and Regular Expressions
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
 
MongoDB Aggregation
MongoDB Aggregation MongoDB Aggregation
MongoDB Aggregation
 
Morel, a Functional Query Language
Morel, a Functional Query LanguageMorel, a Functional Query Language
Morel, a Functional Query Language
 
The Power of Composition (NDC Oslo 2020)
The Power of Composition (NDC Oslo 2020)The Power of Composition (NDC Oslo 2020)
The Power of Composition (NDC Oslo 2020)
 
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional ProgrammingZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
 
Introduction to Functional Programming with Scala
Introduction to Functional Programming with ScalaIntroduction to Functional Programming with Scala
Introduction to Functional Programming with Scala
 
Why functional programming and category theory strongly matters
Why functional programming and category theory strongly mattersWhy functional programming and category theory strongly matters
Why functional programming and category theory strongly matters
 
Prototype programming in JavaScript
Prototype programming in JavaScriptPrototype programming in JavaScript
Prototype programming in JavaScript
 
C# to python
C# to pythonC# to python
C# to python
 
JavaScript - Chapter 15 - Debugging Techniques
 JavaScript - Chapter 15 - Debugging Techniques JavaScript - Chapter 15 - Debugging Techniques
JavaScript - Chapter 15 - Debugging Techniques
 
PHP Cookies and Sessions
PHP Cookies and SessionsPHP Cookies and Sessions
PHP Cookies and Sessions
 
Multiple inheritance in c++
Multiple inheritance in c++Multiple inheritance in c++
Multiple inheritance in c++
 
Node.js middleware: Never again!
Node.js middleware: Never again!Node.js middleware: Never again!
Node.js middleware: Never again!
 
SQLITE Android
SQLITE AndroidSQLITE Android
SQLITE Android
 
Functional Principles for OO Developers
Functional Principles for OO DevelopersFunctional Principles for OO Developers
Functional Principles for OO Developers
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldFunctional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorld
 
Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)
 
Object Calisthenics Applied to PHP
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHP
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and Effects
 

Similar to Event sourcing in the functional world (22 07-2021)

Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenchesLukas Smith
 
Introducing the WSO2 Complex Event Processor
Introducing the WSO2 Complex Event ProcessorIntroducing the WSO2 Complex Event Processor
Introducing the WSO2 Complex Event ProcessorWSO2
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the TrenchesJonathan Wage
 
Andrii Dembitskyi "Events in our applications Event bus and distributed systems"
Andrii Dembitskyi "Events in our applications Event bus and distributed systems"Andrii Dembitskyi "Events in our applications Event bus and distributed systems"
Andrii Dembitskyi "Events in our applications Event bus and distributed systems"Fwdays
 
DDD, CQRS, ES lessons learned
DDD, CQRS, ES lessons learnedDDD, CQRS, ES lessons learned
DDD, CQRS, ES lessons learnedQframe
 
Complex Event Processor 3.0.0 - An overview of upcoming features
Complex Event Processor 3.0.0 - An overview of upcoming features Complex Event Processor 3.0.0 - An overview of upcoming features
Complex Event Processor 3.0.0 - An overview of upcoming features WSO2
 
NET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptxNET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptxpetabridge
 
Oleksandr Valetskyy - DI vs. IoC
Oleksandr Valetskyy - DI vs. IoCOleksandr Valetskyy - DI vs. IoC
Oleksandr Valetskyy - DI vs. IoCOleksandr Valetskyy
 
N Things You Don't Want to Repeat in React Native
N Things You Don't Want to Repeat in React NativeN Things You Don't Want to Repeat in React Native
N Things You Don't Want to Repeat in React NativeAnton Kulyk
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every dayVadym Khondar
 
Online Meetup: Why should container system / platform builders care about con...
Online Meetup: Why should container system / platform builders care about con...Online Meetup: Why should container system / platform builders care about con...
Online Meetup: Why should container system / platform builders care about con...Docker, Inc.
 
Architecting Alive Apps
Architecting Alive AppsArchitecting Alive Apps
Architecting Alive AppsJorge Ortiz
 
An Introduction To CQRS
An Introduction To CQRSAn Introduction To CQRS
An Introduction To CQRSNeil Robbins
 
Event Sourcing - what could go wrong - Devoxx BE
Event Sourcing - what could go wrong - Devoxx BEEvent Sourcing - what could go wrong - Devoxx BE
Event Sourcing - what could go wrong - Devoxx BEAndrzej Ludwikowski
 
Overview of Zookeeper, Helix and Kafka (Oakjug)
Overview of Zookeeper, Helix and Kafka (Oakjug)Overview of Zookeeper, Helix and Kafka (Oakjug)
Overview of Zookeeper, Helix and Kafka (Oakjug)Chris Richardson
 
WSO2 Product Release Webinar - Introducing the WSO2 Complex Event Processor
WSO2 Product Release Webinar - Introducing the WSO2 Complex Event Processor WSO2 Product Release Webinar - Introducing the WSO2 Complex Event Processor
WSO2 Product Release Webinar - Introducing the WSO2 Complex Event Processor WSO2
 
Hack an ASP .NET website? Hard, but possible!
Hack an ASP .NET website? Hard, but possible! Hack an ASP .NET website? Hard, but possible!
Hack an ASP .NET website? Hard, but possible! Vladimir Kochetkov
 
Lightbend Lagom: Microservices Just Right (Scala Days 2016 Berlin)
Lightbend Lagom: Microservices Just Right (Scala Days 2016 Berlin)Lightbend Lagom: Microservices Just Right (Scala Days 2016 Berlin)
Lightbend Lagom: Microservices Just Right (Scala Days 2016 Berlin)mircodotta
 

Similar to Event sourcing in the functional world (22 07-2021) (20)

Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenches
 
Introducing the WSO2 Complex Event Processor
Introducing the WSO2 Complex Event ProcessorIntroducing the WSO2 Complex Event Processor
Introducing the WSO2 Complex Event Processor
 
dSS API by example
dSS API by exampledSS API by example
dSS API by example
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
 
Andrii Dembitskyi "Events in our applications Event bus and distributed systems"
Andrii Dembitskyi "Events in our applications Event bus and distributed systems"Andrii Dembitskyi "Events in our applications Event bus and distributed systems"
Andrii Dembitskyi "Events in our applications Event bus and distributed systems"
 
DDD, CQRS, ES lessons learned
DDD, CQRS, ES lessons learnedDDD, CQRS, ES lessons learned
DDD, CQRS, ES lessons learned
 
Complex Event Processor 3.0.0 - An overview of upcoming features
Complex Event Processor 3.0.0 - An overview of upcoming features Complex Event Processor 3.0.0 - An overview of upcoming features
Complex Event Processor 3.0.0 - An overview of upcoming features
 
NET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptxNET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptx
 
Oleksandr Valetskyy - DI vs. IoC
Oleksandr Valetskyy - DI vs. IoCOleksandr Valetskyy - DI vs. IoC
Oleksandr Valetskyy - DI vs. IoC
 
N Things You Don't Want to Repeat in React Native
N Things You Don't Want to Repeat in React NativeN Things You Don't Want to Repeat in React Native
N Things You Don't Want to Repeat in React Native
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every day
 
Postman On Steroids
Postman On SteroidsPostman On Steroids
Postman On Steroids
 
Online Meetup: Why should container system / platform builders care about con...
Online Meetup: Why should container system / platform builders care about con...Online Meetup: Why should container system / platform builders care about con...
Online Meetup: Why should container system / platform builders care about con...
 
Architecting Alive Apps
Architecting Alive AppsArchitecting Alive Apps
Architecting Alive Apps
 
An Introduction To CQRS
An Introduction To CQRSAn Introduction To CQRS
An Introduction To CQRS
 
Event Sourcing - what could go wrong - Devoxx BE
Event Sourcing - what could go wrong - Devoxx BEEvent Sourcing - what could go wrong - Devoxx BE
Event Sourcing - what could go wrong - Devoxx BE
 
Overview of Zookeeper, Helix and Kafka (Oakjug)
Overview of Zookeeper, Helix and Kafka (Oakjug)Overview of Zookeeper, Helix and Kafka (Oakjug)
Overview of Zookeeper, Helix and Kafka (Oakjug)
 
WSO2 Product Release Webinar - Introducing the WSO2 Complex Event Processor
WSO2 Product Release Webinar - Introducing the WSO2 Complex Event Processor WSO2 Product Release Webinar - Introducing the WSO2 Complex Event Processor
WSO2 Product Release Webinar - Introducing the WSO2 Complex Event Processor
 
Hack an ASP .NET website? Hard, but possible!
Hack an ASP .NET website? Hard, but possible! Hack an ASP .NET website? Hard, but possible!
Hack an ASP .NET website? Hard, but possible!
 
Lightbend Lagom: Microservices Just Right (Scala Days 2016 Berlin)
Lightbend Lagom: Microservices Just Right (Scala Days 2016 Berlin)Lightbend Lagom: Microservices Just Right (Scala Days 2016 Berlin)
Lightbend Lagom: Microservices Just Right (Scala Days 2016 Berlin)
 

Recently uploaded

Construction method of steel structure space frame .pptx
Construction method of steel structure space frame .pptxConstruction method of steel structure space frame .pptx
Construction method of steel structure space frame .pptxwendy cai
 
Electrostatic field in a coaxial transmission line
Electrostatic field in a coaxial transmission lineElectrostatic field in a coaxial transmission line
Electrostatic field in a coaxial transmission lineJulioCesarSalazarHer1
 
An improvement in the safety of big data using blockchain technology
An improvement in the safety of big data using blockchain technologyAn improvement in the safety of big data using blockchain technology
An improvement in the safety of big data using blockchain technologyBOHRInternationalJou1
 
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptxCFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptxR&R Consult
 
A case study of cinema management system project report..pdf
A case study of cinema management system project report..pdfA case study of cinema management system project report..pdf
A case study of cinema management system project report..pdfKamal Acharya
 
A CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdf
A CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdfA CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdf
A CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdfKamal Acharya
 
İTÜ CAD and Reverse Engineering Workshop
İTÜ CAD and Reverse Engineering WorkshopİTÜ CAD and Reverse Engineering Workshop
İTÜ CAD and Reverse Engineering WorkshopEmre Günaydın
 
Online book store management system project.pdf
Online book store management system project.pdfOnline book store management system project.pdf
Online book store management system project.pdfKamal Acharya
 
Fruit shop management system project report.pdf
Fruit shop management system project report.pdfFruit shop management system project report.pdf
Fruit shop management system project report.pdfKamal Acharya
 
RESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdf
RESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdfRESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdf
RESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdfKamal Acharya
 
School management system project report.pdf
School management system project report.pdfSchool management system project report.pdf
School management system project report.pdfKamal Acharya
 
Explosives Industry manufacturing process.pdf
Explosives Industry manufacturing process.pdfExplosives Industry manufacturing process.pdf
Explosives Industry manufacturing process.pdf884710SadaqatAli
 
Natalia Rutkowska - BIM School Course in Kraków
Natalia Rutkowska - BIM School Course in KrakówNatalia Rutkowska - BIM School Course in Kraków
Natalia Rutkowska - BIM School Course in Krakówbim.edu.pl
 
2024 DevOps Pro Europe - Growing at the edge
2024 DevOps Pro Europe - Growing at the edge2024 DevOps Pro Europe - Growing at the edge
2024 DevOps Pro Europe - Growing at the edgePaco Orozco
 
Digital Signal Processing Lecture notes n.pdf
Digital Signal Processing Lecture notes n.pdfDigital Signal Processing Lecture notes n.pdf
Digital Signal Processing Lecture notes n.pdfAbrahamGadissa
 
Top 13 Famous Civil Engineering Scientist
Top 13 Famous Civil Engineering ScientistTop 13 Famous Civil Engineering Scientist
Top 13 Famous Civil Engineering Scientistgettygaming1
 
Democratizing Fuzzing at Scale by Abhishek Arya
Democratizing Fuzzing at Scale by Abhishek AryaDemocratizing Fuzzing at Scale by Abhishek Arya
Democratizing Fuzzing at Scale by Abhishek Aryaabh.arya
 
KIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and Clustering
KIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and ClusteringKIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and Clustering
KIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and ClusteringDr. Radhey Shyam
 
ENERGY STORAGE DEVICES INTRODUCTION UNIT-I
ENERGY STORAGE DEVICES  INTRODUCTION UNIT-IENERGY STORAGE DEVICES  INTRODUCTION UNIT-I
ENERGY STORAGE DEVICES INTRODUCTION UNIT-IVigneshvaranMech
 
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptx
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptxCloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptx
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptxMd. Shahidul Islam Prodhan
 

Recently uploaded (20)

Construction method of steel structure space frame .pptx
Construction method of steel structure space frame .pptxConstruction method of steel structure space frame .pptx
Construction method of steel structure space frame .pptx
 
Electrostatic field in a coaxial transmission line
Electrostatic field in a coaxial transmission lineElectrostatic field in a coaxial transmission line
Electrostatic field in a coaxial transmission line
 
An improvement in the safety of big data using blockchain technology
An improvement in the safety of big data using blockchain technologyAn improvement in the safety of big data using blockchain technology
An improvement in the safety of big data using blockchain technology
 
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptxCFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
 
A case study of cinema management system project report..pdf
A case study of cinema management system project report..pdfA case study of cinema management system project report..pdf
A case study of cinema management system project report..pdf
 
A CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdf
A CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdfA CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdf
A CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdf
 
İTÜ CAD and Reverse Engineering Workshop
İTÜ CAD and Reverse Engineering WorkshopİTÜ CAD and Reverse Engineering Workshop
İTÜ CAD and Reverse Engineering Workshop
 
Online book store management system project.pdf
Online book store management system project.pdfOnline book store management system project.pdf
Online book store management system project.pdf
 
Fruit shop management system project report.pdf
Fruit shop management system project report.pdfFruit shop management system project report.pdf
Fruit shop management system project report.pdf
 
RESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdf
RESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdfRESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdf
RESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdf
 
School management system project report.pdf
School management system project report.pdfSchool management system project report.pdf
School management system project report.pdf
 
Explosives Industry manufacturing process.pdf
Explosives Industry manufacturing process.pdfExplosives Industry manufacturing process.pdf
Explosives Industry manufacturing process.pdf
 
Natalia Rutkowska - BIM School Course in Kraków
Natalia Rutkowska - BIM School Course in KrakówNatalia Rutkowska - BIM School Course in Kraków
Natalia Rutkowska - BIM School Course in Kraków
 
2024 DevOps Pro Europe - Growing at the edge
2024 DevOps Pro Europe - Growing at the edge2024 DevOps Pro Europe - Growing at the edge
2024 DevOps Pro Europe - Growing at the edge
 
Digital Signal Processing Lecture notes n.pdf
Digital Signal Processing Lecture notes n.pdfDigital Signal Processing Lecture notes n.pdf
Digital Signal Processing Lecture notes n.pdf
 
Top 13 Famous Civil Engineering Scientist
Top 13 Famous Civil Engineering ScientistTop 13 Famous Civil Engineering Scientist
Top 13 Famous Civil Engineering Scientist
 
Democratizing Fuzzing at Scale by Abhishek Arya
Democratizing Fuzzing at Scale by Abhishek AryaDemocratizing Fuzzing at Scale by Abhishek Arya
Democratizing Fuzzing at Scale by Abhishek Arya
 
KIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and Clustering
KIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and ClusteringKIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and Clustering
KIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and Clustering
 
ENERGY STORAGE DEVICES INTRODUCTION UNIT-I
ENERGY STORAGE DEVICES  INTRODUCTION UNIT-IENERGY STORAGE DEVICES  INTRODUCTION UNIT-I
ENERGY STORAGE DEVICES INTRODUCTION UNIT-I
 
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptx
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptxCloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptx
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptx
 

Event sourcing in the functional world (22 07-2021)

  • 1. Event Sourcing in the Functional World A practical journey of using F# and event sourcing
  • 2. ● Why Event Sourcing? ● Domain Design: Event Storming ● Primitives, Events, Commands, and Aggregates ● Domain Logic as Pure Functions ● Serialization and DTOs ● Error Handling: Railway-Oriented Programming ● Cosmos DB: Event / Aggregate Store ● Strong(-er) Consistency at Scale ● Assembling the Pipeline: Dependency Rejection ● Change Feed: Eventually Consistent Read Models https://fsharpforfunandprofit.com/
  • 3. Why Event Sourcing? • We need a reliable audit log • We need to know the the state of the system at a given point in time • We believe it helps us to build more scalable systems* (your mileage may vary) “Event Sourcing - Step by step in F#” https://medium.com/@dzoukr/event -sourcing-step-by-step-in-f- be808aa0ca18 “Scaling Event- Sourcing at Jet” https://medium.com/@eulerfx/scalin g-event-sourcing-at-jet- 9c873cac33b8
  • 4. Event Storming Form a shared mental model, avoid technical jargon Focus on business events, not data structures Discover Events, Commands, and Domain Aggregates
  • 5. Primitives The modelling building blocks Simple Wrappers type FirstName = FirstName of string type LastName = LastName of string type MiddleName = MiddleName of string Wrappers with guarded access (recommended) type EmailAddress = private EmailAddress of string [<RequireQualifiedAccess>] module EmailAddress = let create email = if Regex.IsMatch(email, ".*?@(.*)") then email |> EmailAddress |> Ok else Error "Incorrect email format" let value (EmailAddress e) = e Composition type ContactDetails = { Name: Name; Email: EmailAddress } and Name = { FirstName: FirstName MiddleName: MiddleName option LastName: LastName }
  • 6. Events What happened to the system? Different event types are just DU cases type VaccinationEvent = | ContactRegistered of ContactDetails | AppointmentCreated of VaccinationAppointment | AppointmentCanceled of AppointmentId | VaccineAdministered of AppointmentId | ObservationComplete of AppointmentId * ObservationStatus | SurveySubmitted of AppointmentId * SurveyResult Metadata: ● Timestamp ● Aggregate Id ● Sequence No. ● Correlation Id ● Causation Id ● Grouping attributes and VaccinationAppointment = { Id: AppointmentId Vaccine: VaccineType Hub: VaccinationHub Date: DateTime } and AppointmentId = string and VaccineType = Pfizer | Moderna | AstraZeneca and ObservationStatus = | NoAdverseReaction | AdverseReaction of ReactionKind and VaccinationHub = exn and ReactionKind = exn and SurveyResult = exn type Event<'D, 'M> = { Id: Guid; Data: 'D; Metadata: 'M }
  • 7. What caused the change? (Sometimes) Who and why requested the change? Commands Commands are intents, and reflect actions. Naming: usually “VerbNoun” type VaccinationCommand = | RegisterContact of ContactDetails | CreateAppointment of VaccinationAppointment | CancelAppointment of AppointmentId | AdministerVaccine of AppointmentId * ObservationStatus option | SubmitSurvey of AppointmentId * SurveyResult Valid commands generate events let toEvents c = function | RegisterContact cd -> [ ContactRegistered cd ] | CreateAppointment a -> [ AppointmentCreated a ] | CancelAppointment appId -> [ AppointmentCanceled appId ] | AdministerVaccine (appId, None) -> [ VaccineAdministered appId ] | AdministerVaccine (appId, Some status) -> [ VaccineAdministered appId ObservationComplete (appId, status) ] | SubmitSurvey (appId, s) -> [ SurveySubmitted (appId, s) ] Event.Metadata.CausationId = Command.Id
  • 8. “Designing with types: Making illegal states unrepresentable” https://fsharpforfunandprofit.com/po sts/designing-with-types-making- illegal-states-unrepresentable/ Domain Aggregates Aggregates should have enough information to power business rules type VaccineRecipient = { Id: Guid ContactDetails: ContactDetails RegistrationDate: DateTime State: VaccineRecipientState } and VaccineRecipientState = | Registered | Booked of VaccinationAppointment nlist | InProcess of VaccinationInProcess | FullyVaccinated of VaccinationResult nlist and VaccinationInProcess = { Administered: VaccinationResult nlist Booked: VaccinationAppointment nlist } and VaccinationResult = VaccinationAppointment * ObservationStatus option * SurveyResult option and 'T nlist = NonEmptyList<'T> and NonEmptyList<'T> = 'T list
  • 9. Pure functions Domain Logic Event sourcing in a nutshell type Folder<'Aggregate, 'Event> = 'Aggregate -> 'Event list -> 'Aggregate type Handler<'Aggregate, 'Command, 'Event> = 'Aggregate -> 'Command -> 'Aggregate * 'Event list Add error handling, separate “create” and “update” let create newId timestamp event = match event with | ContactRegistered c -> { Id = newId; ContactDetails = c; RegistrationDate = timestamp; State = Registered } |> Ok | _ -> Error "Aggregate doesn't exist" let update aggregate event = match aggregate.State, event with | Registered, AppointmentCreated apt -> { aggregate with State = Booked [ apt ] } |> Ok | Booked list, AppointmentCreated apt -> { aggregate with State = Booked (list @ [apt] ) } |> Ok | _, _ -> "Exercise left to the reader" |> Error
  • 10. “Serializing your domain model” https://fsharpforfunandprofit. com/posts/serializing-your- domain-model/ DTOs DTOs reflect unvalidated input outside of our control type NameDto = { FirstName: string MiddleName: string // can be null :( LastName: string } module NameDto = let toDomain (name: NameDto) = ({ FirstName = name.FirstName |> FirstName MiddleName = name.MiddleName |> Option.ofObj |> Option.map MiddleName LastName = name.LastName |> LastName }: Name) |> Ok You might need to apply versioning to your DTOs type VaccinationEventDto = | AppointmentCreated of VaccinationAppointment | AppointmentCreatedV2 of VaccinationAppointment * Confirmation Events are stored forever, design them carefully!
  • 11. Error Handling Error Handling is tedious type ContactDetailsDto = { Name: NameDto; Email: string } module ContactDetailsDto = let toDomainTheBoringWay contact = let nameResult = contact.Name |> NameDto.toDomain match (nameResult) with | Ok name -> let emailResult = contact.Email |> EmailAddress.create match emailResult with | Ok email -> ({ Name = name; Email = email }: ContactDetails) |> Ok | Error e -> Error e | Error e -> Error e “Railway Oriented Programming” https://fsharpforfunand profit.com/rop/ FsToolkit.ErrorHandling helps module ContactDetailsDto = let toDomain contact = result { let! name = contact.Name |> NameDto.toDomain let! email = contact.Email |> EmailAddress.create return ({ Name = name; Email = email }: ContactDetails) }
  • 12. There and back again Libraries: FSharp.Json https://github.com/vsaprono v/FSharp.Json Thoth.Json (works with Fable!) https://thoth- org.github.io/Thoth.Json/ Serialization FSharp.Json can handle wrapper types and options let record: Name = { FirstName = FirstName "John" MiddleName = None LastName = LastName "Smith" } record |> Json.serialize |> printfn "%s" { "FirstName": "John", "MiddleName": null, "LastName": "Smith" } JSON -> DTO can fail let tryParse<'Data> s = try Json.deserialize<'Data> s |> Ok with | x -> sprintf "Cannot deserialize: %s" x.Message |> Error JSON -> Domain? Compose using ROP let readContacts contactsString = contactsString |> tryParse<ContactDetailsDto> |> Result.bind ContactDetailsDto.toDomain
  • 13. CosmosDB: Event / Aggregate Store Persistence Define partition strategy in the code, not DB let! response = database.CreateContainerIfNotExistsAsync( containerName, "/partitionKey") module VaccinationRecipientKeyStrategy = let toPartitionKey streamId = $"VaccinationRecipient-{streamId}" Partition boundaries determine consistency Stream 1 (partition 1) Event 1 Event 2 Event 3 Aggregate Stream 2 (partition 2) Event 1 Event 2 Aggregate
  • 14. Defining Entities in F# Persistence Useful DB Wrappers module Db = type PartitionKey = PartitionKey of string type Id = Id of string type ETag = ETag of string type EntityType = Event | AggregateRoot Use JSON serializer specifics to map to DB fields type PersistentEntity<'Payload> = { [<JsonField("partitionKey")>] PartitionKey: Db.PartitionKey [<JsonField("id")>] Id: Db.Id [<JsonField("etag")>] ETag: Db.ETag option Type: EntityType Payload: 'Payload } Concrete serialized types module VaccinationPersistence = type Event = PersistentEntity<VaccinationEvent> type Aggregate = PersistentEntity<VaccineRecipient>
  • 15. CosmosDB Transactions Persistence Use Batch API to add events + “upsert” the aggregate module CosmosDb = let createBatch (Db.PartitionKey key) (container: Container) = container.CreateTransactionalBatch(PartitionKey(key)) let createAsString (payload: string) (batch: TransactionalBatch) = new MemoryStream(Encoding.UTF8.GetBytes(payload)) |> batch.CreateItemStream let replaceAsString (Db.Id id) (payload: string) (Db.ETag etag) (batch: TransactionalBatch) = let stream = new MemoryStream (Encoding.UTF8.GetBytes(payload)) let options = TransactionalBatchItemRequestOptions() options.IfMatchEtag <- etag batch.ReplaceItemStream(id, stream, options) let executeBatch (batch: TransactionalBatch) = taskResult { let! response = batch.ExecuteAsync() if (response.IsSuccessStatusCode) then return! Ok () else return! Error (response.StatusCode, response.ErrorMessage) } We use tasks for an easier interop with Azure SDKs Optimistic concurrency
  • 16. “Dependency Rejection” https://blog.ploeh.dk/2017/01/ 27/from-dependency- injection-to-dependency- rejection/ “Reinventing the Transaction Script” https://fsharpforfunandprofit. com/transactionscript/ Assembling the Pipeline The Impure - Pure - Impure “sandwich” • Read data • Deserialize DTOs from JSON • Convert DTOs to Domain Models • Generate commands • Run the handler • Convert Results to DTOs • Serialize DTOs to JSON • Persist the output Do not do I/O in pure code, return directives instead module VaccinationExample = type ErrorMessage = string type DomainEvent = Event<VaccinationEvent, EventMetadata> type HandlerResult = | NoChange | InvalidCommand of ErrorMessage | Conflict of ErrorMessage | CreateAggregate of VaccineRecipient * DomainEvent list | UpdateAggregate of VaccineRecipient * DomainEvent list type Handler = VaccineRecipient option -> VaccinationCommand -> HandlerResult
  • 17. Using CosmosDB to build read models Change Feed You can use either: • Azure Functions CosmosDBTrigger (simpler) • or ChangeFeed Processor running in AppServices (more control) You will need to provision: • A separate Lease collection in CosmosDB • The target storage (DB, blobs, SQL DB, etc) Leases ensure events will be processed in sync, but... Events might come in several partitions at once! let groups = docs |> Seq.groupBy (fun d -> d.GetPropertyValue<string> "partitionKey") for (pk, group) in groups do // process by partitions You have to handle errors (make your own DLQ, etc) You can reprocess events from the beginning!