SlideShare a Scribd company logo
1 of 28
Dennis Doomen | @ddoomen | Aviva Solutions | The Continuous Improver
Command Handlers
Commands
Domain Model
Event Store
Events
App
Query Store
Data Access
Projectors
Events
Query HTTP API
Projections
Events
Command HTTP API
Great unit
of testing
Great unit
of testing
Auditability
comes for free
Can look at
the state in
the past
Can be scaled
independently
Autonomous
projections
Aligns well
with Event
Storming
Forces you to
understand your
domain thoroughly
No relational
table structure
anymore.
Can replay old
data against new
rules
More difficult to
envisage domain
relationships
Great for
replication
Application
Command
Service
Correct
Customer
Email Handler
Customer
Unit of
Work
Projector
Data
Access
Layer
Read
Database Write
Database
CorrectCustomerEmailCommand
HTTP API / In-process invocation
Get<Customer>(identity)
Correct Email
Event Store
Load(events)
Apply
Get changes
CustomerEmailCorrectedEvent
Submit changes
History
Dennis Doomen | @ddoomen | The Continuous Improver
NES
Aggregates.NET
SimpleDomain
NStore
EventStore
NEventStore
SQLStreamStore
NStore
NES
Marten
Handelier
Brighter
MediatR (*)
Projac
LiquidProjections (*)
Dennis Doomen | @ddoomen | The Continuous Improver
Application
Domain
NoSQL / RDBMS
OR/M / DAL
Web UI, HTTP API,
etc
Lucene Index
Document Projector
Web UI, HTTP
API, etc
Web UI, HTTP API, etc
Domain
Commands
Events
Event StoreProjections
Projectors
Uses Event
Sourcing
Uses traditional
CRUD
architecture
Indexing-based
architecture
Subcribe
to
webhooks
Coarse-
grained
HTTP
requests.
Bus
Subscribe
Publish coarse-
grained event
Customer #123
Correct Customer
Shipping Address
Command
Customer Shipping
Address Corrected
Event
Event Store
Treat as a
message, not a
type
Order #456
Correct Shipping Address
Command Handler
Application
Customer Shipping Address
Corrected Handler
Order Redirected
Event.
Identity !=
natural key
Identify
partition key
Primitive types
only
Avoid terms like
Create, Update,
Delete, Change.
Avoid property
change events
Don’t use as
inter-domain
contracts
Don’t expose
outside the
domain
Dennis Doomen | @ddoomen | The Continuous Improver
Customer #123
Correct Customer
Shipping Address
Command
Customer Shipping
Address Corrected
Event
Event Store
Order #456
Correct Shipping Address
Command Handler
Application
Order Redirected
Event
Dennis Doomen | @ddoomen | The Continuous Improver
Command Service
Some Command
Handler
Customer #123
Event Store
Customer Created
Event
Get<Customer>(“123”)
Customer
Created Event
Converter
Customer Enrolled
Event
May split or
merge events
Can also run as
part of migration
May change the
identity
Unaffected Events
Dennis Doomen | @ddoomen | The Continuous Improver
Event
Store
Projector Projector
RDBMS
Subscribe
Subscribe
Document
DB
Projector
RDBMS
Subscribe
Raw SQLNHibernate RavenDB
Lookup
Autonomous &
independent
Storage technique
optimized for
projection
No joining of
projections
Avoid reusing the
projections for
multiple purposes
Use aggressive
caching during
rebuilds.
Caching strategy
optimized for
projector
Owned by
projector
Dennis Doomen | @ddoomen | The Continuous Improver
Stream-by-stream
projections during
rebuilds
var mapBuilder = new EventMapBuilder<MyContext, DocumentProjection, string>();
mapBuilder
.Map<WarrantAssignedEvent>()
.AsCreateOf(anEvent => anEvent.Number)
.Using((document, anEvent) =>
{
document.Type = "Warrant";
document.Kind = anEvent.Kind;
document.Country = anEvent.Country;
document.State = anEvent.InitialState;
});
mapBuilder
.Map<StateTransitionedEvent>()
.When(anEvent => anEvent.State != "Closed")
.AsUpdateOf(anEvent => anEvent.DocumentNumber)
.Using((document, anEvent) => document.State = anEvent.State);
Dennis Doomen | @ddoomen | The Continuous Improver
Transaction
Event Event
Transaction
Event Event
Transaction
Event Event
RDBMS
INSERT/UPDATE … WHERE …
Transaction
Event Event
Transaction
Event Event
Transaction
Event Event
RDBMS
INSERT/UPDATE … WHERE …
INSERT/UPDATE … WHERE …
INSERT/UPDATE … WHERE …
Unit of Work
Dennis Doomen | @ddoomen | The Continuous Improver
private async Task<ExceptionResolution> OnException(ProjectionException exception, int attempts)
{
if (IsTransient(exception))
{
if (attempts < 3)
{
await Task.Delay(TimeSpan.FromSeconds(attempts ^ 2));
return ExceptionResolution.Retry;
}
return ExceptionResolution.Abort;
}
else
{
if (exception.TransactionBatch.Count > 1)
{
return ExceptionResolution.RetryIndividual;
}
// Mark projection as corrupt
return ExceptionResolution.Ignore;
}
}
Dennis Doomen | @ddoomen | The Continuous Improver
Event Store
Projector
Subscription
Projection
Subscribes to events
from multiple
aggregates
Lookup
Event Store
Other Projector
Lookup Projector
Subscription
Subscription
Storage
(persistent)
Lookup
Event
Modifier
Modifies events
with lookup data
Receives
extended events
Dennis Doomen | @ddoomen | The Continuous Improver
var stats = new ProjectionStats(() => DateTime.UtcNow);
stats.StoreProperty("CountByDocument", "some setting key", "some value");
stats.LogEvent("CountByDocument", "some significant thing that happened");
stats.TrackProgress("CountByDocument", currentCheckpoint);
float? transactionsPerSecond = stats.GetSpeed("CountByDocument");
TimeSpan? eta = stats.GetTimeToReach("CountByDocument", targetCheckpoint);
public void Configure(IAppBuilder builder)
{
builder.UseStatistics(stats);
}
Dennis Doomen | @ddoomen | The Continuous Improver
IAppBuilder builder = ….
builder.UseStatistics(stats);
GET http://localhost/projectionStats/CountByDocument
{
"projectorId": "CountByDocument",
"lastCheckpoint": 1000,
"lastCheckpointUpdatedUtc": "2018-05-10T10:39:00Z",
"properties": [{
"key": "some setting key",
"value": "some value",
"lastUpdatedUtc": "2018-05-10T10:39:00Z"
}],
"eventsUrl": "http://localhost/projectionStats/CountByDocument/events"
}
Dennis Doomen | @ddoomen | The Continuous Improver
Document #1
Created Event
Event Store
Graph
Projector
Document #1
Closed Event
(other events)
Projector with
active
projections
Archiving
Projector
Start archiving
Document #1
Marked As
Archivable Event
Mark all events
as archivable
Tracks dependencies
between documents
Deletes
projections
related to
Document #1
Can skip all
archivable events
during next
rebuild.
Dennis Doomen | @ddoomen | The Continuous Improver
Event Store
Lucene
Projector
Document #1
Marked As
Archivable Event
Allows projectors
to clean up
Lucene Index
Take snapshot
Purge events
Tombstone
$tombstone
stream
Application
Document
Projector
Search.
Tracks deleted streams
for future references
Stream Tombstoned
Event
Search
Dennis Doomen | @ddoomen | The Continuous Improver
Event Store
Projector
Application
Version 2
Owns schema
(e.g. FluentMigrator)
Version 1
X
Involves down-time until
projections are rebuild
Dennis Doomen | @ddoomen | The Continuous Improver
Event Store
Projector
Application Application
Network Load Balancer
Event Store
Version 1 Version 2
events
Projection ProjectorProjection
bring off-line
Returns HTTP 503
(Service Unavailable)
Dennis Doomen | @ddoomen | The Continuous Improver
Returns HTTP 503
(Service Unavailable)
Dennis Doomen | @ddoomen | The Continuous Improver
Dennis Doomen
@ddoomen
Dennis Doomen | @ddoomen | The Continuous Improver
• The Good, The Bad and the Ugly of Event
Sourcing
https://www.continuousimprover.com/search/label/event%20sourcing
• Effective Aggregate Design (Vaughn Vernon)
http://dddcommunity.org/library/vernon_2011/
• Liquid Projections
https://www.liquidprojections.net
• Distributed Event Sourcing (Slides)
https://www.slideshare.net/dennisdoomen/building-occasionally-
connected-applications-using-event-sourcing
• Data schema changes in an event sourced
system (paper)
https://files.movereem.nl/2017saner-eventsourcing.pdf

More Related Content

What's hot

ETL With Cassandra Streaming Bulk Loading
ETL With Cassandra Streaming Bulk LoadingETL With Cassandra Streaming Bulk Loading
ETL With Cassandra Streaming Bulk Loadingalex_araujo
 
Architectural caching patterns for kubernetes
Architectural caching patterns for kubernetesArchitectural caching patterns for kubernetes
Architectural caching patterns for kubernetesRafał Leszko
 
Bulk Loading into Cassandra
Bulk Loading into CassandraBulk Loading into Cassandra
Bulk Loading into CassandraBrian Hess
 
Lecture 03 - The Data Warehouse and Design
Lecture 03 - The Data Warehouse and Design Lecture 03 - The Data Warehouse and Design
Lecture 03 - The Data Warehouse and Design phanleson
 
Apache Cassandra - Concepts et fonctionnalités
Apache Cassandra - Concepts et fonctionnalitésApache Cassandra - Concepts et fonctionnalités
Apache Cassandra - Concepts et fonctionnalitésRomain Hardouin
 
Utilizing kotlin flows in an android application
Utilizing kotlin flows in an android applicationUtilizing kotlin flows in an android application
Utilizing kotlin flows in an android applicationSeven Peaks Speaks
 
2. Introduction-to-MSSQL-Server.pptx
2. Introduction-to-MSSQL-Server.pptx2. Introduction-to-MSSQL-Server.pptx
2. Introduction-to-MSSQL-Server.pptxAyobamiAdelekeMDM
 
Data Persistence in Android with Room Library
Data Persistence in Android with Room LibraryData Persistence in Android with Room Library
Data Persistence in Android with Room LibraryReinvently
 
#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기Arawn Park
 
XML External Entity (XXE)
XML External Entity (XXE)XML External Entity (XXE)
XML External Entity (XXE)Jay Thakker
 
Workshop 22: React-Redux Middleware
Workshop 22: React-Redux MiddlewareWorkshop 22: React-Redux Middleware
Workshop 22: React-Redux MiddlewareVisual Engineering
 
Presentation db2 best practices for optimal performance
Presentation   db2 best practices for optimal performancePresentation   db2 best practices for optimal performance
Presentation db2 best practices for optimal performancesolarisyougood
 
Building a Big Data Pipeline
Building a Big Data PipelineBuilding a Big Data Pipeline
Building a Big Data PipelineJesus Rodriguez
 
Differentiate Big Data vs Data Warehouse use cases for a cloud solution
Differentiate Big Data vs Data Warehouse use cases for a cloud solutionDifferentiate Big Data vs Data Warehouse use cases for a cloud solution
Differentiate Big Data vs Data Warehouse use cases for a cloud solutionJames Serra
 
Open Metadata and Governance with Apache Atlas
Open Metadata and Governance with Apache AtlasOpen Metadata and Governance with Apache Atlas
Open Metadata and Governance with Apache AtlasDataWorks Summit
 

What's hot (20)

ETL With Cassandra Streaming Bulk Loading
ETL With Cassandra Streaming Bulk LoadingETL With Cassandra Streaming Bulk Loading
ETL With Cassandra Streaming Bulk Loading
 
RDBMS vs NoSQL
RDBMS vs NoSQLRDBMS vs NoSQL
RDBMS vs NoSQL
 
Cassandra Database
Cassandra DatabaseCassandra Database
Cassandra Database
 
Architectural caching patterns for kubernetes
Architectural caching patterns for kubernetesArchitectural caching patterns for kubernetes
Architectural caching patterns for kubernetes
 
Bulk Loading into Cassandra
Bulk Loading into CassandraBulk Loading into Cassandra
Bulk Loading into Cassandra
 
Lecture 03 - The Data Warehouse and Design
Lecture 03 - The Data Warehouse and Design Lecture 03 - The Data Warehouse and Design
Lecture 03 - The Data Warehouse and Design
 
Apache Cassandra - Concepts et fonctionnalités
Apache Cassandra - Concepts et fonctionnalitésApache Cassandra - Concepts et fonctionnalités
Apache Cassandra - Concepts et fonctionnalités
 
Utilizing kotlin flows in an android application
Utilizing kotlin flows in an android applicationUtilizing kotlin flows in an android application
Utilizing kotlin flows in an android application
 
2. Introduction-to-MSSQL-Server.pptx
2. Introduction-to-MSSQL-Server.pptx2. Introduction-to-MSSQL-Server.pptx
2. Introduction-to-MSSQL-Server.pptx
 
Key-Value NoSQL Database
Key-Value NoSQL DatabaseKey-Value NoSQL Database
Key-Value NoSQL Database
 
Data Persistence in Android with Room Library
Data Persistence in Android with Room LibraryData Persistence in Android with Room Library
Data Persistence in Android with Room Library
 
#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기
 
XML External Entity (XXE)
XML External Entity (XXE)XML External Entity (XXE)
XML External Entity (XXE)
 
Modern Data Architecture
Modern Data ArchitectureModern Data Architecture
Modern Data Architecture
 
Workshop 22: React-Redux Middleware
Workshop 22: React-Redux MiddlewareWorkshop 22: React-Redux Middleware
Workshop 22: React-Redux Middleware
 
Presentation db2 best practices for optimal performance
Presentation   db2 best practices for optimal performancePresentation   db2 best practices for optimal performance
Presentation db2 best practices for optimal performance
 
Building a Big Data Pipeline
Building a Big Data PipelineBuilding a Big Data Pipeline
Building a Big Data Pipeline
 
SQL injection: Not only AND 1=1
SQL injection: Not only AND 1=1SQL injection: Not only AND 1=1
SQL injection: Not only AND 1=1
 
Differentiate Big Data vs Data Warehouse use cases for a cloud solution
Differentiate Big Data vs Data Warehouse use cases for a cloud solutionDifferentiate Big Data vs Data Warehouse use cases for a cloud solution
Differentiate Big Data vs Data Warehouse use cases for a cloud solution
 
Open Metadata and Governance with Apache Atlas
Open Metadata and Governance with Apache AtlasOpen Metadata and Governance with Apache Atlas
Open Metadata and Governance with Apache Atlas
 

Similar to Event Sourcing from the Trenches (DDD Europe 2020)

Event Sourcing from the Trenches (with examples from .NET)
Event Sourcing from the Trenches (with examples from .NET)Event Sourcing from the Trenches (with examples from .NET)
Event Sourcing from the Trenches (with examples from .NET)Dennis Doomen
 
The Good, The Bad and The Ugly of Event Sourcing
The Good, The Bad and The Ugly of Event Sourcing The Good, The Bad and The Ugly of Event Sourcing
The Good, The Bad and The Ugly of Event Sourcing Dennis Doomen
 
The Good, The Bad and The Ugly of Event Sourcing
The Good, The Bad and The Ugly of Event SourcingThe Good, The Bad and The Ugly of Event Sourcing
The Good, The Bad and The Ugly of Event SourcingDennis Doomen
 
Decomposing the Monolith using modern-day .NET and a touch of microservices
Decomposing the Monolith using modern-day .NET and a touch of microservicesDecomposing the Monolith using modern-day .NET and a touch of microservices
Decomposing the Monolith using modern-day .NET and a touch of microservicesDennis Doomen
 
Decomposing the Monolith (Riga Dev Days 2019)
Decomposing the Monolith (Riga Dev Days 2019)Decomposing the Monolith (Riga Dev Days 2019)
Decomposing the Monolith (Riga Dev Days 2019)Dennis Doomen
 
Cqrs and event sourcing in azure
Cqrs and event sourcing in azureCqrs and event sourcing in azure
Cqrs and event sourcing in azureSergey Seletsky
 
From Event Receivers to SharePoint Webhooks
From Event Receivers to SharePoint WebhooksFrom Event Receivers to SharePoint Webhooks
From Event Receivers to SharePoint WebhooksAndré Vala
 
Developing functional domain models with event sourcing (sbtb, sbtb2015)
Developing functional domain models with event sourcing (sbtb, sbtb2015)Developing functional domain models with event sourcing (sbtb, sbtb2015)
Developing functional domain models with event sourcing (sbtb, sbtb2015)Chris Richardson
 
Design patterns for Event Sourcing in .NET
Design patterns for Event Sourcing in .NETDesign patterns for Event Sourcing in .NET
Design patterns for Event Sourcing in .NETDennis Doomen
 
The Next Generation Application Server – How Event Based Processing yields s...
The Next Generation  Application Server – How Event Based Processing yields s...The Next Generation  Application Server – How Event Based Processing yields s...
The Next Generation Application Server – How Event Based Processing yields s...Guy Korland
 
Demystifying Reactive Programming
Demystifying Reactive ProgrammingDemystifying Reactive Programming
Demystifying Reactive ProgrammingTom Bulatewicz, PhD
 
From Event Receivers to SharePoint Webhooks (SPS Lisbon 2017)
From Event Receivers to SharePoint Webhooks (SPS Lisbon 2017)From Event Receivers to SharePoint Webhooks (SPS Lisbon 2017)
From Event Receivers to SharePoint Webhooks (SPS Lisbon 2017)André Vala
 
MongoDB.local DC 2018: Scaling Realtime Apps with Change Streams
MongoDB.local DC 2018: Scaling Realtime Apps with Change StreamsMongoDB.local DC 2018: Scaling Realtime Apps with Change Streams
MongoDB.local DC 2018: Scaling Realtime Apps with Change StreamsMongoDB
 
Crafted Design - ITAKE 2014
Crafted Design - ITAKE 2014Crafted Design - ITAKE 2014
Crafted Design - ITAKE 2014Sandro Mancuso
 
React for Re-use: Creating UI Components with Confluence Connect
React for Re-use: Creating UI Components with Confluence ConnectReact for Re-use: Creating UI Components with Confluence Connect
React for Re-use: Creating UI Components with Confluence ConnectAtlassian
 
Reactive Stream Processing Using DDS and Rx
Reactive Stream Processing Using DDS and RxReactive Stream Processing Using DDS and Rx
Reactive Stream Processing Using DDS and RxSumant Tambe
 
Crafted Design - Sandro Mancuso
Crafted Design - Sandro MancusoCrafted Design - Sandro Mancuso
Crafted Design - Sandro MancusoJAXLondon2014
 
Crafted Design - LJC World Tour Mash Up 2014
Crafted Design - LJC World Tour Mash Up 2014Crafted Design - LJC World Tour Mash Up 2014
Crafted Design - LJC World Tour Mash Up 2014Sandro Mancuso
 
DDD, CQRS, ES lessons learned
DDD, CQRS, ES lessons learnedDDD, CQRS, ES lessons learned
DDD, CQRS, ES lessons learnedQframe
 

Similar to Event Sourcing from the Trenches (DDD Europe 2020) (20)

Event Sourcing from the Trenches (with examples from .NET)
Event Sourcing from the Trenches (with examples from .NET)Event Sourcing from the Trenches (with examples from .NET)
Event Sourcing from the Trenches (with examples from .NET)
 
The Good, The Bad and The Ugly of Event Sourcing
The Good, The Bad and The Ugly of Event Sourcing The Good, The Bad and The Ugly of Event Sourcing
The Good, The Bad and The Ugly of Event Sourcing
 
The Good, The Bad and The Ugly of Event Sourcing
The Good, The Bad and The Ugly of Event SourcingThe Good, The Bad and The Ugly of Event Sourcing
The Good, The Bad and The Ugly of Event Sourcing
 
Decomposing the Monolith using modern-day .NET and a touch of microservices
Decomposing the Monolith using modern-day .NET and a touch of microservicesDecomposing the Monolith using modern-day .NET and a touch of microservices
Decomposing the Monolith using modern-day .NET and a touch of microservices
 
CQRS and Event Sourcing
CQRS and Event SourcingCQRS and Event Sourcing
CQRS and Event Sourcing
 
Decomposing the Monolith (Riga Dev Days 2019)
Decomposing the Monolith (Riga Dev Days 2019)Decomposing the Monolith (Riga Dev Days 2019)
Decomposing the Monolith (Riga Dev Days 2019)
 
Cqrs and event sourcing in azure
Cqrs and event sourcing in azureCqrs and event sourcing in azure
Cqrs and event sourcing in azure
 
From Event Receivers to SharePoint Webhooks
From Event Receivers to SharePoint WebhooksFrom Event Receivers to SharePoint Webhooks
From Event Receivers to SharePoint Webhooks
 
Developing functional domain models with event sourcing (sbtb, sbtb2015)
Developing functional domain models with event sourcing (sbtb, sbtb2015)Developing functional domain models with event sourcing (sbtb, sbtb2015)
Developing functional domain models with event sourcing (sbtb, sbtb2015)
 
Design patterns for Event Sourcing in .NET
Design patterns for Event Sourcing in .NETDesign patterns for Event Sourcing in .NET
Design patterns for Event Sourcing in .NET
 
The Next Generation Application Server – How Event Based Processing yields s...
The Next Generation  Application Server – How Event Based Processing yields s...The Next Generation  Application Server – How Event Based Processing yields s...
The Next Generation Application Server – How Event Based Processing yields s...
 
Demystifying Reactive Programming
Demystifying Reactive ProgrammingDemystifying Reactive Programming
Demystifying Reactive Programming
 
From Event Receivers to SharePoint Webhooks (SPS Lisbon 2017)
From Event Receivers to SharePoint Webhooks (SPS Lisbon 2017)From Event Receivers to SharePoint Webhooks (SPS Lisbon 2017)
From Event Receivers to SharePoint Webhooks (SPS Lisbon 2017)
 
MongoDB.local DC 2018: Scaling Realtime Apps with Change Streams
MongoDB.local DC 2018: Scaling Realtime Apps with Change StreamsMongoDB.local DC 2018: Scaling Realtime Apps with Change Streams
MongoDB.local DC 2018: Scaling Realtime Apps with Change Streams
 
Crafted Design - ITAKE 2014
Crafted Design - ITAKE 2014Crafted Design - ITAKE 2014
Crafted Design - ITAKE 2014
 
React for Re-use: Creating UI Components with Confluence Connect
React for Re-use: Creating UI Components with Confluence ConnectReact for Re-use: Creating UI Components with Confluence Connect
React for Re-use: Creating UI Components with Confluence Connect
 
Reactive Stream Processing Using DDS and Rx
Reactive Stream Processing Using DDS and RxReactive Stream Processing Using DDS and Rx
Reactive Stream Processing Using DDS and Rx
 
Crafted Design - Sandro Mancuso
Crafted Design - Sandro MancusoCrafted Design - Sandro Mancuso
Crafted Design - Sandro Mancuso
 
Crafted Design - LJC World Tour Mash Up 2014
Crafted Design - LJC World Tour Mash Up 2014Crafted Design - LJC World Tour Mash Up 2014
Crafted Design - LJC World Tour Mash Up 2014
 
DDD, CQRS, ES lessons learned
DDD, CQRS, ES lessons learnedDDD, CQRS, ES lessons learned
DDD, CQRS, ES lessons learned
 

More from Dennis Doomen

Getting a grip on your code dependencies (2023-10)
Getting a grip on your code dependencies (2023-10)Getting a grip on your code dependencies (2023-10)
Getting a grip on your code dependencies (2023-10)Dennis Doomen
 
Tools and practices to help you deal with legacy code
Tools and practices to help you deal with legacy codeTools and practices to help you deal with legacy code
Tools and practices to help you deal with legacy codeDennis Doomen
 
What you can learn from an open-source project with 250 million downloads
What you can learn from an open-source project with 250 million downloadsWhat you can learn from an open-source project with 250 million downloads
What you can learn from an open-source project with 250 million downloadsDennis Doomen
 
Getting a grip on your code dependencies
Getting a grip on your code dependenciesGetting a grip on your code dependencies
Getting a grip on your code dependenciesDennis Doomen
 
My Laws of Test Driven Development (2023)
My Laws of Test Driven Development (2023)My Laws of Test Driven Development (2023)
My Laws of Test Driven Development (2023)Dennis Doomen
 
Automate Infrastructure with Pulumi and C#
Automate Infrastructure with Pulumi and C#Automate Infrastructure with Pulumi and C#
Automate Infrastructure with Pulumi and C#Dennis Doomen
 
What is the right unit in unit testing (UpdateConf 2022)
What is the right unit in unit testing (UpdateConf 2022)What is the right unit in unit testing (UpdateConf 2022)
What is the right unit in unit testing (UpdateConf 2022)Dennis Doomen
 
50 things software teams should not do.pptx
50 things software teams should not do.pptx50 things software teams should not do.pptx
50 things software teams should not do.pptxDennis Doomen
 
What is the right "unit" in unit testing and why it is not a class?
What is the right "unit" in unit testing and why it is not a class?What is the right "unit" in unit testing and why it is not a class?
What is the right "unit" in unit testing and why it is not a class?Dennis Doomen
 
A lab around the principles and practices for writing maintainable code
A lab around the principles and practices for writing maintainable codeA lab around the principles and practices for writing maintainable code
A lab around the principles and practices for writing maintainable codeDennis Doomen
 
How to Practice TDD Without Shooting Yourself in the Foot
How to Practice TDD Without Shooting Yourself in the FootHow to Practice TDD Without Shooting Yourself in the Foot
How to Practice TDD Without Shooting Yourself in the FootDennis Doomen
 
Practical introduction to DDD, CQRS and Event Sourcing
Practical introduction to DDD, CQRS and Event SourcingPractical introduction to DDD, CQRS and Event Sourcing
Practical introduction to DDD, CQRS and Event SourcingDennis Doomen
 
How to practice TDD without shooting yourself in the foot
How to practice TDD without shooting yourself in the footHow to practice TDD without shooting yourself in the foot
How to practice TDD without shooting yourself in the footDennis Doomen
 
A lab around the principles and practices for writing maintainable code (2019)
A lab around the principles and practices for writing maintainable code (2019)A lab around the principles and practices for writing maintainable code (2019)
A lab around the principles and practices for writing maintainable code (2019)Dennis Doomen
 
Lessons learned from two decades of professional software development
Lessons learned from two decades of professional software developmentLessons learned from two decades of professional software development
Lessons learned from two decades of professional software developmentDennis Doomen
 
How To Practice TDD Without Shooting Yourself In The Foot
How To Practice TDD Without Shooting Yourself In The FootHow To Practice TDD Without Shooting Yourself In The Foot
How To Practice TDD Without Shooting Yourself In The FootDennis Doomen
 
Decomposing the monolith into embeddable microservices using OWIN, WebHooks, ...
Decomposing the monolith into embeddable microservices using OWIN, WebHooks, ...Decomposing the monolith into embeddable microservices using OWIN, WebHooks, ...
Decomposing the monolith into embeddable microservices using OWIN, WebHooks, ...Dennis Doomen
 
Strengths and weaknesses of dependency injection
Strengths and weaknesses of dependency injectionStrengths and weaknesses of dependency injection
Strengths and weaknesses of dependency injectionDennis Doomen
 
Build Libraries That People Love To use
Build Libraries That People Love To useBuild Libraries That People Love To use
Build Libraries That People Love To useDennis Doomen
 
Decomposing the Monolith using Microservices that don't give you pain
Decomposing the Monolith using Microservices that don't give you painDecomposing the Monolith using Microservices that don't give you pain
Decomposing the Monolith using Microservices that don't give you painDennis Doomen
 

More from Dennis Doomen (20)

Getting a grip on your code dependencies (2023-10)
Getting a grip on your code dependencies (2023-10)Getting a grip on your code dependencies (2023-10)
Getting a grip on your code dependencies (2023-10)
 
Tools and practices to help you deal with legacy code
Tools and practices to help you deal with legacy codeTools and practices to help you deal with legacy code
Tools and practices to help you deal with legacy code
 
What you can learn from an open-source project with 250 million downloads
What you can learn from an open-source project with 250 million downloadsWhat you can learn from an open-source project with 250 million downloads
What you can learn from an open-source project with 250 million downloads
 
Getting a grip on your code dependencies
Getting a grip on your code dependenciesGetting a grip on your code dependencies
Getting a grip on your code dependencies
 
My Laws of Test Driven Development (2023)
My Laws of Test Driven Development (2023)My Laws of Test Driven Development (2023)
My Laws of Test Driven Development (2023)
 
Automate Infrastructure with Pulumi and C#
Automate Infrastructure with Pulumi and C#Automate Infrastructure with Pulumi and C#
Automate Infrastructure with Pulumi and C#
 
What is the right unit in unit testing (UpdateConf 2022)
What is the right unit in unit testing (UpdateConf 2022)What is the right unit in unit testing (UpdateConf 2022)
What is the right unit in unit testing (UpdateConf 2022)
 
50 things software teams should not do.pptx
50 things software teams should not do.pptx50 things software teams should not do.pptx
50 things software teams should not do.pptx
 
What is the right "unit" in unit testing and why it is not a class?
What is the right "unit" in unit testing and why it is not a class?What is the right "unit" in unit testing and why it is not a class?
What is the right "unit" in unit testing and why it is not a class?
 
A lab around the principles and practices for writing maintainable code
A lab around the principles and practices for writing maintainable codeA lab around the principles and practices for writing maintainable code
A lab around the principles and practices for writing maintainable code
 
How to Practice TDD Without Shooting Yourself in the Foot
How to Practice TDD Without Shooting Yourself in the FootHow to Practice TDD Without Shooting Yourself in the Foot
How to Practice TDD Without Shooting Yourself in the Foot
 
Practical introduction to DDD, CQRS and Event Sourcing
Practical introduction to DDD, CQRS and Event SourcingPractical introduction to DDD, CQRS and Event Sourcing
Practical introduction to DDD, CQRS and Event Sourcing
 
How to practice TDD without shooting yourself in the foot
How to practice TDD without shooting yourself in the footHow to practice TDD without shooting yourself in the foot
How to practice TDD without shooting yourself in the foot
 
A lab around the principles and practices for writing maintainable code (2019)
A lab around the principles and practices for writing maintainable code (2019)A lab around the principles and practices for writing maintainable code (2019)
A lab around the principles and practices for writing maintainable code (2019)
 
Lessons learned from two decades of professional software development
Lessons learned from two decades of professional software developmentLessons learned from two decades of professional software development
Lessons learned from two decades of professional software development
 
How To Practice TDD Without Shooting Yourself In The Foot
How To Practice TDD Without Shooting Yourself In The FootHow To Practice TDD Without Shooting Yourself In The Foot
How To Practice TDD Without Shooting Yourself In The Foot
 
Decomposing the monolith into embeddable microservices using OWIN, WebHooks, ...
Decomposing the monolith into embeddable microservices using OWIN, WebHooks, ...Decomposing the monolith into embeddable microservices using OWIN, WebHooks, ...
Decomposing the monolith into embeddable microservices using OWIN, WebHooks, ...
 
Strengths and weaknesses of dependency injection
Strengths and weaknesses of dependency injectionStrengths and weaknesses of dependency injection
Strengths and weaknesses of dependency injection
 
Build Libraries That People Love To use
Build Libraries That People Love To useBuild Libraries That People Love To use
Build Libraries That People Love To use
 
Decomposing the Monolith using Microservices that don't give you pain
Decomposing the Monolith using Microservices that don't give you painDecomposing the Monolith using Microservices that don't give you pain
Decomposing the Monolith using Microservices that don't give you pain
 

Recently uploaded

AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embeddingZilliz
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesZilliz
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 

Recently uploaded (20)

AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embedding
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector Databases
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 

Event Sourcing from the Trenches (DDD Europe 2020)

  • 1. Dennis Doomen | @ddoomen | Aviva Solutions | The Continuous Improver
  • 2.
  • 3.
  • 4.
  • 5.
  • 6. Command Handlers Commands Domain Model Event Store Events App Query Store Data Access Projectors Events Query HTTP API Projections Events Command HTTP API Great unit of testing Great unit of testing Auditability comes for free Can look at the state in the past Can be scaled independently Autonomous projections Aligns well with Event Storming Forces you to understand your domain thoroughly No relational table structure anymore. Can replay old data against new rules More difficult to envisage domain relationships Great for replication
  • 7. Application Command Service Correct Customer Email Handler Customer Unit of Work Projector Data Access Layer Read Database Write Database CorrectCustomerEmailCommand HTTP API / In-process invocation Get<Customer>(identity) Correct Email Event Store Load(events) Apply Get changes CustomerEmailCorrectedEvent Submit changes History Dennis Doomen | @ddoomen | The Continuous Improver NES Aggregates.NET SimpleDomain NStore EventStore NEventStore SQLStreamStore NStore NES Marten Handelier Brighter MediatR (*) Projac LiquidProjections (*)
  • 8. Dennis Doomen | @ddoomen | The Continuous Improver Application Domain NoSQL / RDBMS OR/M / DAL Web UI, HTTP API, etc Lucene Index Document Projector Web UI, HTTP API, etc Web UI, HTTP API, etc Domain Commands Events Event StoreProjections Projectors Uses Event Sourcing Uses traditional CRUD architecture Indexing-based architecture Subcribe to webhooks Coarse- grained HTTP requests. Bus Subscribe Publish coarse- grained event
  • 9. Customer #123 Correct Customer Shipping Address Command Customer Shipping Address Corrected Event Event Store Treat as a message, not a type Order #456 Correct Shipping Address Command Handler Application Customer Shipping Address Corrected Handler Order Redirected Event. Identity != natural key Identify partition key Primitive types only Avoid terms like Create, Update, Delete, Change. Avoid property change events Don’t use as inter-domain contracts Don’t expose outside the domain Dennis Doomen | @ddoomen | The Continuous Improver
  • 10. Customer #123 Correct Customer Shipping Address Command Customer Shipping Address Corrected Event Event Store Order #456 Correct Shipping Address Command Handler Application Order Redirected Event Dennis Doomen | @ddoomen | The Continuous Improver
  • 11. Command Service Some Command Handler Customer #123 Event Store Customer Created Event Get<Customer>(“123”) Customer Created Event Converter Customer Enrolled Event May split or merge events Can also run as part of migration May change the identity Unaffected Events Dennis Doomen | @ddoomen | The Continuous Improver
  • 12. Event Store Projector Projector RDBMS Subscribe Subscribe Document DB Projector RDBMS Subscribe Raw SQLNHibernate RavenDB Lookup Autonomous & independent Storage technique optimized for projection No joining of projections Avoid reusing the projections for multiple purposes Use aggressive caching during rebuilds. Caching strategy optimized for projector Owned by projector Dennis Doomen | @ddoomen | The Continuous Improver Stream-by-stream projections during rebuilds
  • 13. var mapBuilder = new EventMapBuilder<MyContext, DocumentProjection, string>(); mapBuilder .Map<WarrantAssignedEvent>() .AsCreateOf(anEvent => anEvent.Number) .Using((document, anEvent) => { document.Type = "Warrant"; document.Kind = anEvent.Kind; document.Country = anEvent.Country; document.State = anEvent.InitialState; }); mapBuilder .Map<StateTransitionedEvent>() .When(anEvent => anEvent.State != "Closed") .AsUpdateOf(anEvent => anEvent.DocumentNumber) .Using((document, anEvent) => document.State = anEvent.State); Dennis Doomen | @ddoomen | The Continuous Improver
  • 14. Transaction Event Event Transaction Event Event Transaction Event Event RDBMS INSERT/UPDATE … WHERE … Transaction Event Event Transaction Event Event Transaction Event Event RDBMS INSERT/UPDATE … WHERE … INSERT/UPDATE … WHERE … INSERT/UPDATE … WHERE … Unit of Work Dennis Doomen | @ddoomen | The Continuous Improver
  • 15. private async Task<ExceptionResolution> OnException(ProjectionException exception, int attempts) { if (IsTransient(exception)) { if (attempts < 3) { await Task.Delay(TimeSpan.FromSeconds(attempts ^ 2)); return ExceptionResolution.Retry; } return ExceptionResolution.Abort; } else { if (exception.TransactionBatch.Count > 1) { return ExceptionResolution.RetryIndividual; } // Mark projection as corrupt return ExceptionResolution.Ignore; } } Dennis Doomen | @ddoomen | The Continuous Improver
  • 16. Event Store Projector Subscription Projection Subscribes to events from multiple aggregates Lookup
  • 17. Event Store Other Projector Lookup Projector Subscription Subscription Storage (persistent) Lookup Event Modifier Modifies events with lookup data Receives extended events Dennis Doomen | @ddoomen | The Continuous Improver
  • 18. var stats = new ProjectionStats(() => DateTime.UtcNow); stats.StoreProperty("CountByDocument", "some setting key", "some value"); stats.LogEvent("CountByDocument", "some significant thing that happened"); stats.TrackProgress("CountByDocument", currentCheckpoint); float? transactionsPerSecond = stats.GetSpeed("CountByDocument"); TimeSpan? eta = stats.GetTimeToReach("CountByDocument", targetCheckpoint); public void Configure(IAppBuilder builder) { builder.UseStatistics(stats); } Dennis Doomen | @ddoomen | The Continuous Improver
  • 19. IAppBuilder builder = …. builder.UseStatistics(stats); GET http://localhost/projectionStats/CountByDocument { "projectorId": "CountByDocument", "lastCheckpoint": 1000, "lastCheckpointUpdatedUtc": "2018-05-10T10:39:00Z", "properties": [{ "key": "some setting key", "value": "some value", "lastUpdatedUtc": "2018-05-10T10:39:00Z" }], "eventsUrl": "http://localhost/projectionStats/CountByDocument/events" } Dennis Doomen | @ddoomen | The Continuous Improver
  • 20. Document #1 Created Event Event Store Graph Projector Document #1 Closed Event (other events) Projector with active projections Archiving Projector Start archiving Document #1 Marked As Archivable Event Mark all events as archivable Tracks dependencies between documents Deletes projections related to Document #1 Can skip all archivable events during next rebuild. Dennis Doomen | @ddoomen | The Continuous Improver
  • 21. Event Store Lucene Projector Document #1 Marked As Archivable Event Allows projectors to clean up Lucene Index Take snapshot Purge events Tombstone $tombstone stream Application Document Projector Search. Tracks deleted streams for future references Stream Tombstoned Event Search Dennis Doomen | @ddoomen | The Continuous Improver
  • 22. Event Store Projector Application Version 2 Owns schema (e.g. FluentMigrator) Version 1 X Involves down-time until projections are rebuild Dennis Doomen | @ddoomen | The Continuous Improver
  • 23. Event Store Projector Application Application Network Load Balancer Event Store Version 1 Version 2 events Projection ProjectorProjection bring off-line Returns HTTP 503 (Service Unavailable) Dennis Doomen | @ddoomen | The Continuous Improver Returns HTTP 503 (Service Unavailable)
  • 24.
  • 25.
  • 26. Dennis Doomen | @ddoomen | The Continuous Improver
  • 27. Dennis Doomen @ddoomen Dennis Doomen | @ddoomen | The Continuous Improver
  • 28. • The Good, The Bad and the Ugly of Event Sourcing https://www.continuousimprover.com/search/label/event%20sourcing • Effective Aggregate Design (Vaughn Vernon) http://dddcommunity.org/library/vernon_2011/ • Liquid Projections https://www.liquidprojections.net • Distributed Event Sourcing (Slides) https://www.slideshare.net/dennisdoomen/building-occasionally- connected-applications-using-event-sourcing • Data schema changes in an event sourced system (paper) https://files.movereem.nl/2017saner-eventsourcing.pdf