SlideShare a Scribd company logo
1 of 19
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

Alphorm.com Formation SOPHOS XG FIREWALL: Les fondamentaux
Alphorm.com Formation SOPHOS XG FIREWALL: Les fondamentauxAlphorm.com Formation SOPHOS XG FIREWALL: Les fondamentaux
Alphorm.com Formation SOPHOS XG FIREWALL: Les fondamentauxAlphorm
 
Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8RichardWarburton
 
Functional and Event Driven - another approach to domain modeling
Functional and Event Driven - another approach to domain modelingFunctional and Event Driven - another approach to domain modeling
Functional and Event Driven - another approach to domain modelingDebasish Ghosh
 
Clean architecture with ddd layering in php
Clean architecture with ddd layering in phpClean architecture with ddd layering in php
Clean architecture with ddd layering in phpLeonardo Proietti
 
Hexagonal Symfony - SymfonyCon Amsterdam 2019
Hexagonal Symfony - SymfonyCon Amsterdam 2019Hexagonal Symfony - SymfonyCon Amsterdam 2019
Hexagonal Symfony - SymfonyCon Amsterdam 2019Matthias Noback
 
Web sémantique et Ecommerce
Web sémantique et EcommerceWeb sémantique et Ecommerce
Web sémantique et EcommerceAntidot
 
Những nguyên tắc cơ bản xây dựng hệ thống an toàn thông tin trong doanh nghiệp
Những nguyên tắc cơ bản xây dựng hệ thống an toàn thông tin trong doanh nghiệpNhững nguyên tắc cơ bản xây dựng hệ thống an toàn thông tin trong doanh nghiệp
Những nguyên tắc cơ bản xây dựng hệ thống an toàn thông tin trong doanh nghiệpCIO Vietnam
 
Oops concepts in php
Oops concepts in phpOops concepts in php
Oops concepts in phpCPD INDIA
 
Testing Spring Boot application in post-JUnit 4 world
Testing Spring Boot application in post-JUnit 4 worldTesting Spring Boot application in post-JUnit 4 world
Testing Spring Boot application in post-JUnit 4 worldYura Nosenko
 
Developing Java Streaming Applications with Apache Storm
Developing Java Streaming Applications with Apache StormDeveloping Java Streaming Applications with Apache Storm
Developing Java Streaming Applications with Apache StormLester Martin
 
Append only data stores
Append only data storesAppend only data stores
Append only data storesJan Kronquist
 
Slide An toàn mạng nâng cao PTIT
Slide An toàn mạng nâng cao PTITSlide An toàn mạng nâng cao PTIT
Slide An toàn mạng nâng cao PTITNguynMinh294
 
Trucos gta por gamezox33
Trucos gta  por gamezox33Trucos gta  por gamezox33
Trucos gta por gamezox33gamezox33
 
Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)Scott Wlaschin
 
Beezo Share - Đồ Án Thực Tập Công Cụ Giám Sát Mạng Python
Beezo Share - Đồ Án Thực Tập Công Cụ Giám Sát Mạng PythonBeezo Share - Đồ Án Thực Tập Công Cụ Giám Sát Mạng Python
Beezo Share - Đồ Án Thực Tập Công Cụ Giám Sát Mạng PythonBeezo
 
Advanced javascript
Advanced javascriptAdvanced javascript
Advanced javascriptDoeun KOCH
 

What's hot (20)

Alphorm.com Formation SOPHOS XG FIREWALL: Les fondamentaux
Alphorm.com Formation SOPHOS XG FIREWALL: Les fondamentauxAlphorm.com Formation SOPHOS XG FIREWALL: Les fondamentaux
Alphorm.com Formation SOPHOS XG FIREWALL: Les fondamentaux
 
Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8
 
Functional and Event Driven - another approach to domain modeling
Functional and Event Driven - another approach to domain modelingFunctional and Event Driven - another approach to domain modeling
Functional and Event Driven - another approach to domain modeling
 
Clean architecture with ddd layering in php
Clean architecture with ddd layering in phpClean architecture with ddd layering in php
Clean architecture with ddd layering in php
 
Hexagonal Symfony - SymfonyCon Amsterdam 2019
Hexagonal Symfony - SymfonyCon Amsterdam 2019Hexagonal Symfony - SymfonyCon Amsterdam 2019
Hexagonal Symfony - SymfonyCon Amsterdam 2019
 
Vulkan 1.1 Reference Guide
Vulkan 1.1 Reference GuideVulkan 1.1 Reference Guide
Vulkan 1.1 Reference Guide
 
Web sémantique et Ecommerce
Web sémantique et EcommerceWeb sémantique et Ecommerce
Web sémantique et Ecommerce
 
JavaScript Inheritance
JavaScript InheritanceJavaScript Inheritance
JavaScript Inheritance
 
Những nguyên tắc cơ bản xây dựng hệ thống an toàn thông tin trong doanh nghiệp
Những nguyên tắc cơ bản xây dựng hệ thống an toàn thông tin trong doanh nghiệpNhững nguyên tắc cơ bản xây dựng hệ thống an toàn thông tin trong doanh nghiệp
Những nguyên tắc cơ bản xây dựng hệ thống an toàn thông tin trong doanh nghiệp
 
Oops concepts in php
Oops concepts in phpOops concepts in php
Oops concepts in php
 
Testing Spring Boot application in post-JUnit 4 world
Testing Spring Boot application in post-JUnit 4 worldTesting Spring Boot application in post-JUnit 4 world
Testing Spring Boot application in post-JUnit 4 world
 
jBPM v7 Roadmap
jBPM v7 RoadmapjBPM v7 Roadmap
jBPM v7 Roadmap
 
Developing Java Streaming Applications with Apache Storm
Developing Java Streaming Applications with Apache StormDeveloping Java Streaming Applications with Apache Storm
Developing Java Streaming Applications with Apache Storm
 
PHP MVC
PHP MVCPHP MVC
PHP MVC
 
Append only data stores
Append only data storesAppend only data stores
Append only data stores
 
Slide An toàn mạng nâng cao PTIT
Slide An toàn mạng nâng cao PTITSlide An toàn mạng nâng cao PTIT
Slide An toàn mạng nâng cao PTIT
 
Trucos gta por gamezox33
Trucos gta  por gamezox33Trucos gta  por gamezox33
Trucos gta por gamezox33
 
Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)
 
Beezo Share - Đồ Án Thực Tập Công Cụ Giám Sát Mạng Python
Beezo Share - Đồ Án Thực Tập Công Cụ Giám Sát Mạng PythonBeezo Share - Đồ Án Thực Tập Công Cụ Giám Sát Mạng Python
Beezo Share - Đồ Án Thực Tập Công Cụ Giám Sát Mạng Python
 
Advanced javascript
Advanced javascriptAdvanced javascript
Advanced javascript
 

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

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
 
Seismic Hazard Assessment Software in Python by Prof. Dr. Costas Sachpazis
Seismic Hazard Assessment Software in Python by Prof. Dr. Costas SachpazisSeismic Hazard Assessment Software in Python by Prof. Dr. Costas Sachpazis
Seismic Hazard Assessment Software in Python by Prof. Dr. Costas SachpazisDr.Costas Sachpazis
 
ONLINE CAR SERVICING SYSTEM PROJECT REPORT.pdf
ONLINE CAR SERVICING SYSTEM PROJECT REPORT.pdfONLINE CAR SERVICING SYSTEM PROJECT REPORT.pdf
ONLINE CAR SERVICING SYSTEM PROJECT REPORT.pdfKamal Acharya
 
Furniture showroom management system project.pdf
Furniture showroom management system project.pdfFurniture showroom management system project.pdf
Furniture showroom management system project.pdfKamal Acharya
 
Object Oriented Programming OOP Lab Manual.docx
Object Oriented Programming OOP Lab Manual.docxObject Oriented Programming OOP Lab Manual.docx
Object Oriented Programming OOP Lab Manual.docxRashidFaridChishti
 
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
 
ROAD CONSTRUCTION PRESENTATION.PPTX.pptx
ROAD CONSTRUCTION PRESENTATION.PPTX.pptxROAD CONSTRUCTION PRESENTATION.PPTX.pptx
ROAD CONSTRUCTION PRESENTATION.PPTX.pptxGagandeepKaur617299
 
Attraction and Repulsion type Moving Iron Instruments.pptx
Attraction and Repulsion type Moving Iron Instruments.pptxAttraction and Repulsion type Moving Iron Instruments.pptx
Attraction and Repulsion type Moving Iron Instruments.pptxkarthikeyanS725446
 
DR PROF ING GURUDUTT SAHNI WIKIPEDIA.pdf
DR PROF ING GURUDUTT SAHNI WIKIPEDIA.pdfDR PROF ING GURUDUTT SAHNI WIKIPEDIA.pdf
DR PROF ING GURUDUTT SAHNI WIKIPEDIA.pdfDrGurudutt
 
Complex plane, Modulus, Argument, Graphical representation of a complex numbe...
Complex plane, Modulus, Argument, Graphical representation of a complex numbe...Complex plane, Modulus, Argument, Graphical representation of a complex numbe...
Complex plane, Modulus, Argument, Graphical representation of a complex numbe...MohammadAliNayeem
 
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
 
Diploma Engineering Drawing Qp-2024 Ece .pdf
Diploma Engineering Drawing Qp-2024 Ece .pdfDiploma Engineering Drawing Qp-2024 Ece .pdf
Diploma Engineering Drawing Qp-2024 Ece .pdfJNTUA
 
The battle for RAG, explore the pros and cons of using KnowledgeGraphs and Ve...
The battle for RAG, explore the pros and cons of using KnowledgeGraphs and Ve...The battle for RAG, explore the pros and cons of using KnowledgeGraphs and Ve...
The battle for RAG, explore the pros and cons of using KnowledgeGraphs and Ve...Roi Lipman
 
ANSI(ST)-III_Manufacturing-I_05052020.pdf
ANSI(ST)-III_Manufacturing-I_05052020.pdfANSI(ST)-III_Manufacturing-I_05052020.pdf
ANSI(ST)-III_Manufacturing-I_05052020.pdfBertinKamsipa1
 
Supermarket billing system project report..pdf
Supermarket billing system project report..pdfSupermarket billing system project report..pdf
Supermarket billing system project report..pdfKamal Acharya
 
RM&IPR M5 notes.pdfResearch Methodolgy & Intellectual Property Rights Series 5
RM&IPR M5 notes.pdfResearch Methodolgy & Intellectual Property Rights Series 5RM&IPR M5 notes.pdfResearch Methodolgy & Intellectual Property Rights Series 5
RM&IPR M5 notes.pdfResearch Methodolgy & Intellectual Property Rights Series 5T.D. Shashikala
 
Intelligent Agents, A discovery on How A Rational Agent Acts
Intelligent Agents, A discovery on How A Rational Agent ActsIntelligent Agents, A discovery on How A Rational Agent Acts
Intelligent Agents, A discovery on How A Rational Agent ActsSheetal Jain
 
Dairy management system project report..pdf
Dairy management system project report..pdfDairy management system project report..pdf
Dairy management 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
 
KIT-601 Lecture Notes-UNIT-5.pdf Frame Works and Visualization
KIT-601 Lecture Notes-UNIT-5.pdf Frame Works and VisualizationKIT-601 Lecture Notes-UNIT-5.pdf Frame Works and Visualization
KIT-601 Lecture Notes-UNIT-5.pdf Frame Works and VisualizationDr. Radhey Shyam
 

Recently uploaded (20)

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
 
Seismic Hazard Assessment Software in Python by Prof. Dr. Costas Sachpazis
Seismic Hazard Assessment Software in Python by Prof. Dr. Costas SachpazisSeismic Hazard Assessment Software in Python by Prof. Dr. Costas Sachpazis
Seismic Hazard Assessment Software in Python by Prof. Dr. Costas Sachpazis
 
ONLINE CAR SERVICING SYSTEM PROJECT REPORT.pdf
ONLINE CAR SERVICING SYSTEM PROJECT REPORT.pdfONLINE CAR SERVICING SYSTEM PROJECT REPORT.pdf
ONLINE CAR SERVICING SYSTEM PROJECT REPORT.pdf
 
Furniture showroom management system project.pdf
Furniture showroom management system project.pdfFurniture showroom management system project.pdf
Furniture showroom management system project.pdf
 
Object Oriented Programming OOP Lab Manual.docx
Object Oriented Programming OOP Lab Manual.docxObject Oriented Programming OOP Lab Manual.docx
Object Oriented Programming OOP Lab Manual.docx
 
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
 
ROAD CONSTRUCTION PRESENTATION.PPTX.pptx
ROAD CONSTRUCTION PRESENTATION.PPTX.pptxROAD CONSTRUCTION PRESENTATION.PPTX.pptx
ROAD CONSTRUCTION PRESENTATION.PPTX.pptx
 
Attraction and Repulsion type Moving Iron Instruments.pptx
Attraction and Repulsion type Moving Iron Instruments.pptxAttraction and Repulsion type Moving Iron Instruments.pptx
Attraction and Repulsion type Moving Iron Instruments.pptx
 
DR PROF ING GURUDUTT SAHNI WIKIPEDIA.pdf
DR PROF ING GURUDUTT SAHNI WIKIPEDIA.pdfDR PROF ING GURUDUTT SAHNI WIKIPEDIA.pdf
DR PROF ING GURUDUTT SAHNI WIKIPEDIA.pdf
 
Complex plane, Modulus, Argument, Graphical representation of a complex numbe...
Complex plane, Modulus, Argument, Graphical representation of a complex numbe...Complex plane, Modulus, Argument, Graphical representation of a complex numbe...
Complex plane, Modulus, Argument, Graphical representation of a complex numbe...
 
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
 
Diploma Engineering Drawing Qp-2024 Ece .pdf
Diploma Engineering Drawing Qp-2024 Ece .pdfDiploma Engineering Drawing Qp-2024 Ece .pdf
Diploma Engineering Drawing Qp-2024 Ece .pdf
 
The battle for RAG, explore the pros and cons of using KnowledgeGraphs and Ve...
The battle for RAG, explore the pros and cons of using KnowledgeGraphs and Ve...The battle for RAG, explore the pros and cons of using KnowledgeGraphs and Ve...
The battle for RAG, explore the pros and cons of using KnowledgeGraphs and Ve...
 
ANSI(ST)-III_Manufacturing-I_05052020.pdf
ANSI(ST)-III_Manufacturing-I_05052020.pdfANSI(ST)-III_Manufacturing-I_05052020.pdf
ANSI(ST)-III_Manufacturing-I_05052020.pdf
 
Supermarket billing system project report..pdf
Supermarket billing system project report..pdfSupermarket billing system project report..pdf
Supermarket billing system project report..pdf
 
RM&IPR M5 notes.pdfResearch Methodolgy & Intellectual Property Rights Series 5
RM&IPR M5 notes.pdfResearch Methodolgy & Intellectual Property Rights Series 5RM&IPR M5 notes.pdfResearch Methodolgy & Intellectual Property Rights Series 5
RM&IPR M5 notes.pdfResearch Methodolgy & Intellectual Property Rights Series 5
 
Intelligent Agents, A discovery on How A Rational Agent Acts
Intelligent Agents, A discovery on How A Rational Agent ActsIntelligent Agents, A discovery on How A Rational Agent Acts
Intelligent Agents, A discovery on How A Rational Agent Acts
 
Dairy management system project report..pdf
Dairy management system project report..pdfDairy management system project report..pdf
Dairy management 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
 
KIT-601 Lecture Notes-UNIT-5.pdf Frame Works and Visualization
KIT-601 Lecture Notes-UNIT-5.pdf Frame Works and VisualizationKIT-601 Lecture Notes-UNIT-5.pdf Frame Works and Visualization
KIT-601 Lecture Notes-UNIT-5.pdf Frame Works and Visualization
 

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!