Staff Software Engineer. Passionate about DDD, CQRS, Event Sourcing and Distributed Systems.
Kayaking, too.
Maybe. Depends how you're using it.
Kafka can be an event store, but I know several teams using it just for streaming, not sourcing.
Looks similar, but it's not the same thing!
#EventSourcing #Kafka
Maybe. Depends how you're using it.
Kafka can be an event store, but I know several teams using it just for streaming, not sourcing.
Looks similar, but it's not the same thing!
#EventSourcing #Kafka
This WILL happen. Plan for it!
Common strategy:
1. Build new projection (ProjectionV2)
2. Fill it from events
3. Switch traffic once caught up
4. Delete old projection
Projections are disposable.
They’re just views of events.
This WILL happen. Plan for it!
Common strategy:
1. Build new projection (ProjectionV2)
2. Fill it from events
3. Switch traffic once caught up
4. Delete old projection
Projections are disposable.
They’re just views of events.
Events IN: OrderPlaced, PaymentReceived
Events OUT: ReservationRequested, ShipmentScheduled
Orchestration with full history and testable like any aggregate
It's events all the way down
Events IN: OrderPlaced, PaymentReceived
Events OUT: ReservationRequested, ShipmentScheduled
Orchestration with full history and testable like any aggregate
It's events all the way down
Year 2: Needs "shippingMethod"
But, old events?
1. Upcasting: transform old events on read
2. Weak schema: new fields optional
3. Multiple event types: OrderPlacedV2
Each works:
- Weak schema = simple
- Upcasting = control
Year 2: Needs "shippingMethod"
But, old events?
1. Upcasting: transform old events on read
2. Weak schema: new fields optional
3. Multiple event types: OrderPlacedV2
Each works:
- Weak schema = simple
- Upcasting = control
Update projection → SUCCESS
Save checkpoint → CRASH
Checkpoint is now behind the data.
Restart = reprocessing.
Fix: Store checkpoint with projection data, update both in one transaction.
Either both succeed or both fail.
No inconsistency.
Update projection → SUCCESS
Save checkpoint → CRASH
Checkpoint is now behind the data.
Restart = reprocessing.
Fix: Store checkpoint with projection data, update both in one transaction.
Either both succeed or both fail.
No inconsistency.
Your projection processes event 458,295 and crashes before saving checkpoint.
On restart it processes 458,295 again.
If that duplicates data, you have a problem.
Already processed? Skip it or make replaying give same result.
Design for safe replays
Your projection processes event 458,295 and crashes before saving checkpoint.
On restart it processes 458,295 again.
If that duplicates data, you have a problem.
Already processed? Skip it or make replaying give same result.
Design for safe replays
When it restarts, where does it begin?
Checkpoints solve this: periodically save "processed up to event 458,295"
Restart resumes from the checkpoint.
No checkpoint? You replay from the beginning.
Trade-off: checkpoint frequency vs replay cost.
When it restarts, where does it begin?
Checkpoints solve this: periodically save "processed up to event 458,295"
Restart resumes from the checkpoint.
No checkpoint? You replay from the beginning.
Trade-off: checkpoint frequency vs replay cost.
Doctors don't UPDATE past entries, they add corrections.
March 15: "Patient reports knee pain"
March 16: "Diagnosis: sprain"
March 20: "Corrected diagnosis: torn meniscus"
All three facts are true. All three stay in the record.
Just like Event Sourcing.
Doctors don't UPDATE past entries, they add corrections.
March 15: "Patient reports knee pain"
March 16: "Diagnosis: sprain"
March 20: "Corrected diagnosis: torn meniscus"
All three facts are true. All three stay in the record.
Just like Event Sourcing.
- CustomerAgeCalculated
- InventoryReserved
Private domain implementation. Can change anytime
External Events:
- OrderPlaced
- PaymentReceived
Published API. Version instead.
Exposing internals creates distributed coupling.
Keep them separate.
- CustomerAgeCalculated
- InventoryReserved
Private domain implementation. Can change anytime
External Events:
- OrderPlaced
- PaymentReceived
Published API. Version instead.
Exposing internals creates distributed coupling.
Keep them separate.
youtu.be/eD7ls4KynRo
youtu.be/eD7ls4KynRo
One stream = One aggregate
Customer[123] stream → CustomerRegistered, EmailChanged, AddressUpdated
Order[456] stream → OrderPlaced, ItemAdded, OrderShipped
Different streams. Different things.
One stream = One aggregate
Customer[123] stream → CustomerRegistered, EmailChanged, AddressUpdated
Order[456] stream → OrderPlaced, ItemAdded, OrderShipped
Different streams. Different things.
Thus your workflow is its own log.
Thus your workflow is its own log.
OrderAccepted → OrderPaid → OrderShipped
All share CorrelationId: "ABC123"
Now you can... 👇
OrderAccepted → OrderPaid → OrderShipped
All share CorrelationId: "ABC123"
Now you can... 👇
They want a relational schema.
Solution: project events into a dedicated reporting database!
Why? 👇
They want a relational schema.
Solution: project events into a dedicated reporting database!
Why? 👇
Optimistic concurrency with expected version:
1. Load events from stream (version 67)
2. Process command, generate new events
3. Write with expectedVersion=67
And then... 👇
Optimistic concurrency with expected version:
1. Load events from stream (version 67)
2. Process command, generate new events
3. Write with expectedVersion=67
And then... 👇
"But I need to DELETE something!".
Here's the reality: you don't delete events.
You record a NEW event that something was deleted/cancelled/revoked.
OrderPlaced → OrderCancelled
Both facts are true. History doesn't have an undo button.
"But I need to DELETE something!".
Here's the reality: you don't delete events.
You record a NEW event that something was deleted/cancelled/revoked.
OrderPlaced → OrderCancelled
Both facts are true. History doesn't have an undo button.
Given: [OrderPlaced, ItemAdded]
When: RemoveItem
Then: [ItemRemoved]
This reads like a spec. It IS a spec.
Your tests become the living documentation of your business rules.
Given: [OrderPlaced, ItemAdded]
When: RemoveItem
Then: [ItemRemoved]
This reads like a spec. It IS a spec.
Your tests become the living documentation of your business rules.
Kinda like a database view, but better:
- You can have dozens of them
- Optimised for specific queries
- Rebuild anytime from events
- Different databases even
Kinda like a database view, but better:
- You can have dozens of them
- Optimised for specific queries
- Rebuild anytime from events
- Different databases even
ted.dev/talks
#Java #EventSourcing #cqrs
ted.dev/talks
#Java #EventSourcing #cqrs
You don't. Not directly.
This is where CQRS (Command Query Responsibility Segregation) enters.
Commands write events → void return
Queries read → no mutations
Separate concerns. Separate models. Separate scaling.
You don't. Not directly.
This is where CQRS (Command Query Responsibility Segregation) enters.
Commands write events → void return
Queries read → no mutations
Separate concerns. Separate models. Separate scaling.
Event Sourcing: Save the story, derive the structure
Your domain object's structure becomes transient.
Storage format ≠ Application format
Event Sourcing: Save the story, derive the structure
Your domain object's structure becomes transient.
Storage format ≠ Application format
1. Write every FACT that happens in your system on sticky notes
2. Put them on a wall
3. Group related ones together
You'll have hundreds. That's your event model.
(this works great even if you DON'T use Event Sourcing)
1. Write every FACT that happens in your system on sticky notes
2. Put them on a wall
3. Group related ones together
You'll have hundreds. That's your event model.
(this works great even if you DON'T use Event Sourcing)
Named with past tense verbs: `CustomerRegistered` or `OrderPlaced`.
Events are immutable, which makes sense when you think about it because you can't change the past.
This principle is critical to Event Sourcing
Named with past tense verbs: `CustomerRegistered` or `OrderPlaced`.
Events are immutable, which makes sense when you think about it because you can't change the past.
This principle is critical to Event Sourcing
This is why it can be hard to know WHY data changed when you only store WHAT has changed.
This is why it can be hard to know WHY data changed when you only store WHAT has changed.