@koenighotze
Eventsourcing
You are doing it wrong
@koenighotze
Eventsourcing
You are doing it wrong because I know best
@koenighotze
Eventsourcing - You are maybe doing parts
of it wrong because we made some mistakes
along the way and so will you, I guess. This
is difficult because there are no easy right/
wrong answers; only trade-offs.
@koenighotze
Eventsourcing
You are probably doing parts of it wrong
@koenighotze#Devoxx #EventSourcingFTW
David Schmitz
Senacor Technologies
@koenighotze
#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
Are YOU building microservices?
Are YOU doing Domain Driven Design?
Are YOU applying eventsourcing?
Are YOU using Kafka as an eventstore?
@koenighotze#Devoxx #EventSourcingFTW
Typical misconceptions
Patterns “we” found useful
Traps to avoid
Not a Kafka-rant
What works for us, might not work for you
…and the other way around
@koenighotze#Devoxx #EventSourcingFTW
What we’ll cover!
@koenighotze
Eventsourcing bootcamp
@koenighotze#Devoxx #EventSourcingFTW
Eventsourcing from an
architects perspective
@koenighotze#Devoxx #EventSourcingFTW
MicroserviceMicroservice Microservice
How do microservices
communicate?
@koenighotze#Devoxx #EventSourcingFTW
MicroserviceMicroservice Microservice
SUPER EXPENSIVE DATABASE
THAT ONLY SCALES
VERTICALLY
@koenighotze#Devoxx #EventSourcingFTW
MicroserviceMicroservice Microservice
DB DB DB
@koenighotze#Devoxx #EventSourcingFTW
Kafka - the Golden Source of Truth
Microservice
Read
modelEvents
Microservice
Read
modelEvents
Microservice
Read
modelEvents
THE DATA LAKE!
@koenighotze#Devoxx #EventSourcingFTW
Kafka
Microservice
Read
modelEvents
Microservice
Read
modelEvents
Microservice
Read
modelEvents
THE DATA LAKE!
Never mind the details.
That is eventsourcing
magic.
Just do the Right Thing!
@koenighotze#Devoxx #EventSourcingFTW
Just, the magician
@koenighotze#Devoxx #EventSourcingFTW
The idea in basic terms
@koenighotze#Devoxx #EventSourcingFTW
Domain driven design
Event driven architecture
Distributed systems
@koenighotze#Devoxx #EventSourcingFTW
Start with your domain
Find the bounded contexts
Find the important concepts
Find the dynamics in your Business Language
@koenighotze#Devoxx #EventSourcingFTW
Aggregate
Event
Stream
Eventstore
Read model Projection
Stored as data and event type,
typically ordered in creation
order
Stored in eventstore using
unique identifiers
Derives current
state from
stream of events
Hydrated events
result in
builds
can be stored as a
@koenighotze#Devoxx #EventSourcingFTW
David at 19-11-11T08:11
User Onboarded
User-David
The current David
@koenighotze#Devoxx #EventSourcingFTW
User-9714de5c
UserOnboarded
email: foo@bar.de
address: …
UserRegistered
userId: 9714de5c…
UserRelocated
newAddress: …
Direction of time
@koenighotze#Devoxx #EventSourcingFTW
User-9714de5c
UserOnboarded
email: foo@bar.de
address: …
UserRegistered
userId: 9714de5c…
UserRelocated
newAddress: …
Stream
@koenighotze#Devoxx #EventSourcingFTW
User-9714de5c
UserOnboarded
email: foo@bar.de
address: …
UserRegistered
userId: 9714de5c…
UserRelocated
newAddress: …
Event
@koenighotze#Devoxx #EventSourcingFTW
User-9714de5c
UserOnboarded
email: foo@bar.de
address: …
UserRegistered
userId: 9714de5c…
UserRelocated
newAddress: …
Event Payload
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
Current User Projection
handle User
Created
handle User
Onboarded
handle User
Relocated
…
Projections
hydrate the
state stored
in a stream
@koenighotze#Devoxx #EventSourcingFTW
User Aggregate
userId: 9714de5c…
email: foo@bar.de
address: <new address>
Represents
the user at
the time of
the last
event read
Current User Projection
handle User
Created
handle User
Onboarded
handle User
Relocated
…
@koenighotze#Devoxx #EventSourcingFTW
Eventsourcing helps answering the question of
dealing with data in distributed systems in a
scalable way
It makes the dynamics of your systems explicit as
first class concepts
@koenighotze#Devoxx #EventSourcingFTW
Decoupling on a business level
@koenighotze
Read models
…and why you may not need to them (initially)
@koenighotze#Devoxx #EventSourcingFTW
Read models are local hydrates of the
events stored in specific streams
@koenighotze#Devoxx #EventSourcingFTW
readModel = Stream.of(events)
.leftFold(handlers)
@koenighotze#Devoxx #EventSourcingFTW
How can we handle read models?
@koenighotze#Devoxx #EventSourcingFTW
Just use a local database
@koenighotze#Devoxx #EventSourcingFTW
User
Microservice
Read
model
(SQL)
user id
last event
id
user name email street
9741… 3 David foo@bar.de …
4532… 7 Martin null …
@koenighotze#Devoxx #EventSourcingFTW
User
Microservice
Read
model
(SQL)
user id
last event
id
user name email street
9741… 4 David qux@ba.ze …
4532… 7 Martin null …
@koenighotze#Devoxx #EventSourcingFTW
User
Microservice
Read
model
(SQL)
Neo4J
@koenighotze#Devoxx #EventSourcingFTW
Challenges?
@koenighotze#Devoxx #EventSourcingFTW
Eventual consistency
Replays and rebuilds
Re-deliveries
Operational complexity
@koenighotze#Devoxx #EventSourcingFTW
You may not need a read model
@koenighotze#Devoxx #EventSourcingFTW
Typical strategies for storing events
@koenighotze#Devoxx #EventSourcingFTW
Maybe all events of an aggregate type in a
single stream?
@koenighotze#Devoxx #EventSourcingFTW
Users
User A User B User C User D
@koenighotze#Devoxx #EventSourcingFTW
User-A User-C User-D
Users
User-B
Better: One stream per
aggregate
@koenighotze#Devoxx #EventSourcingFTW
User
Microservice
GET /users/B
User-B
@koenighotze#Devoxx #EventSourcingFTW
User
Microservice
GET /users/B
User-B
@koenighotze#Devoxx #EventSourcingFTW
User
Microservice
GET /users/B
User-B
@koenighotze#Devoxx #EventSourcingFTW
Consistent read
No operational overhead
Super-simple programming logic
@koenighotze#Devoxx #EventSourcingFTW
But, what about speed?
@koenighotze#Devoxx #EventSourcingFTW
readEntity 100: 66.047ms
@koenighotze#Devoxx #EventSourcingFTW
But, what about queries?
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
Build specific
query projection
@koenighotze#Devoxx #EventSourcingFTW
$ct-Users
@koenighotze#Devoxx #EventSourcingFTW
User-A User-C User-D
Adult-Users-Projection
User-B
@koenighotze#Devoxx #EventSourcingFTW
User-A User-C User-D
Adult-Users-Projection
User-B
@koenighotze#Devoxx #EventSourcingFTW
event handlers are not CPU or IO intensive
Prefer small aggregates
Measure and introduce persistent read
models only if needed
@koenighotze
Transactions, concurrency and your eventstore
@koenighotze#Devoxx #EventSourcingFTW
How can we guarantee correctness
when writing?
@koenighotze#Devoxx #EventSourcingFTW
“Only withdraw money, if the bank
account holds enough money!”*
*Actually, a real bank would not want such a business
rule. They earn money if you overdraw your account. An
overdraft fee is one of the most expensive fees banks
charge. Just saying…
@koenighotze#Devoxx #EventSourcingFTW
The correctness of the
business result depends
on the order of events
@koenighotze#Devoxx #EventSourcingFTW
MoneyWithdrawn
amount: 97 EUR
MoneyDeposited
amount: 100 EUR
MoneyDeposited
amount: 50 EUR
100 EUR
MoneyWithdrawn
amount: 10 EUR 90 EUR
-7 EUR
140 EUR
@koenighotze#Devoxx #EventSourcingFTW
Let’s shuffle
@koenighotze#Devoxx #EventSourcingFTW
140 EUR
MoneyWithdrawn
amount: 97 EUR
MoneyWithdrawn
amount: 10 EUR
MoneyDeposited
amount: 100 EUR
MoneyDeposited
amount: 50 EUR
100 EUR
90 EUR
43 EUR
@koenighotze#Devoxx #EventSourcingFTW
I can use a readmodel for
fast validation, can I?
@koenighotze#Devoxx #EventSourcingFTW
Maybe not
@koenighotze#Devoxx #EventSourcingFTW
Are YOUR systems single-threaded?
@koenighotze#Devoxx #EventSourcingFTW
Account
Microservice
Read
model
(SQL)
MoneyWithdrawn
Check if
account holds
enough money
Account
Microservice
MoneyWithdrawn
Check if
account holds
enough money
@koenighotze#Devoxx #EventSourcingFTW
Validation against a read model
is prone to inconsistencies
@koenighotze#Devoxx #EventSourcingFTW
Just manage
transactions with a
database
@koenighotze#Devoxx #EventSourcingFTW
https://www.confluent.io/blog/messaging-single-source-truth/
@koenighotze#Devoxx #EventSourcingFTW
Quick tip for finding friends in ops:
Ask them to just install and maintain
production grade Kafka and Couchbase
installations in the Cloud
@koenighotze#Devoxx #EventSourcingFTW
The aggregate is responsible for
enforcing business invariants
@koenighotze#Devoxx #EventSourcingFTW
MoneyWithdrawn
amount: 50 EUR
I want in!
Not if you mess up my
internal business rules!
Account-123
Current account balance:
30 EUR
@koenighotze#Devoxx #EventSourcingFTW
The aggregate is the
“Transaction Boundary”
@koenighotze#Devoxx #EventSourcingFTW
https://en.wikipedia.org/wiki/Optimistic_concurrency_control
@koenighotze#Devoxx #EventSourcingFTW
https://en.wikipedia.org/wiki/Optimistic_concurrency_control
OCC assumes that multiple transactions
can frequently complete without
interfering with each other.
@koenighotze#Devoxx #EventSourcingFTW
https://en.wikipedia.org/wiki/Optimistic_concurrency_control
…each transaction verifies that no other
transaction has modified the data it has
read…
@koenighotze#Devoxx #EventSourcingFTW
The happy path
@koenighotze#Devoxx #EventSourcingFTW
Account
lastEventNumber: 5
Account-123
MoneyDeposited
eventNumber: 5
amount: 100 EUR
… … …
MoneyWithdrawn
amount: 10 EUR
holderId: …
accountNumber: …
amount: …
@koenighotze#Devoxx #EventSourcingFTW
Account-123
MoneyWithdrawn
amount: 10 EUR
Account
lastEventNumber: 5
holderId: …
accountNumber: …
amount: …
MoneyDeposited
eventNumber: 5
amount: 100 EUR
… … …
Account.lastEventNumber(5)
===
Stream.lastEventNumber(5)
@koenighotze#Devoxx #EventSourcingFTW
Account
lastEventNumber: 5
Account-123
MoneyDeposited
eventNumber: 5
amount: 100 EUR
MoneyWithdrawn
amount: 10 EUR
holderId: …
accountNumber: …
amount: …
eventNumber: 6
… … …
@koenighotze#Devoxx #EventSourcingFTW
The not-so-happy path
@koenighotze#Devoxx #EventSourcingFTW
Account
lastEventNumber: 4
Account-123
MoneyDeposited
eventNumber: 4
amount: 10 EUR
… … …
holderId: …
accountNumber: …
amount: …
@koenighotze#Devoxx #EventSourcingFTW
Account-123
… … …
MoneyDeposited
eventNumber: 4
amount: 10 EUR
Account
lastEventNumber: 4
holderId: …
accountNumber: …
amount: …
Account.lastEventNumber(4)
!==
Stream.lastEventNumber(5)
MoneyWithdrawn
amount: 97 EUR
MoneyDeposited
eventNumber: 5
amount: 100 EUR
@koenighotze#Devoxx #EventSourcingFTW
Account
Microservice
PUT /account/1234
account-1234
@koenighotze#Devoxx #EventSourcingFTW
PUT /account/1234
Account
Microservice
account-1234
@koenighotze#Devoxx #EventSourcingFTW
PUT /account/1234
Account
Microservice
account-1234
@koenighotze#Devoxx #EventSourcingFTW
PUT /account/1234
Account
Microservice
account-1234
@koenighotze#Devoxx #EventSourcingFTW
PUT /account/1234
Account
Microservice
account-1234
@koenighotze#Devoxx #EventSourcingFTW
And Kafka?
@koenighotze#DevExperience18 #10TipsMicroservices#Devoxx #EventSourcingFTW
@koenighotze#DevExperience18 #10TipsMicroservices#Devoxx #EventSourcingFTW
…
@koenighotze#DevExperience18 #10TipsMicroservices#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
Scalability without locks
Consistency
Design choice
Super-simple programming logic
@koenighotze
Versions, up-front-design and
breaking things down the road
@koenighotze#Devoxx #EventSourcingFTW
How can we deal with versions
without going crazy?
@koenighotze#Devoxx #EventSourcingFTW
Just use semantic
versioning and
typed events
@koenighotze#Devoxx #EventSourcingFTW
Account-123
MoneyDepositedAccountOpened events-1.0.0.jar
Producer
Consumer
@koenighotze#Devoxx #EventSourcingFTW
…then change some event types
@koenighotze#Devoxx #EventSourcingFTW
Account-123
……
events-1.0.0.jar
Producer
Consumer
events-2.0.0.jar
MoneyDeposited2
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
Gosh…just apply
“Double Write”
@koenighotze#Devoxx #EventSourcingFTW
Account-123
…… MoneyDeposited MoneyDeposited2
@koenighotze#Devoxx #EventSourcingFTW
Account-123
……
ConsumerShould I process V1?
MoneyDeposited MoneyDeposited2
@koenighotze#Devoxx #EventSourcingFTW
Account-123
……
Consumer
Or wait for a V2…which
might never arrive?
MoneyDeposited MoneyDeposited2
@koenighotze#Devoxx #EventSourcingFTW
MoneyDeposited_v1
MoneyDeposited_v2
…
MoneyDeposited_v100
@koenighotze#Devoxx #EventSourcingFTW
MoneyDeposited_v1Handler
MoneyDeposited_v2Handler
…
MoneyDeposited_v100Handler
@koenighotze#Devoxx #EventSourcingFTW
Upcaster
@koenighotze#Devoxx #EventSourcingFTW
Account-123
A V1.0……
Consumer
Upcaster
A V1.0 B V2.0
A V2.0
A V2.0
f: v1->v2
@koenighotze#Devoxx #EventSourcingFTW
5 months later…
@koenighotze#Devoxx #EventSourcingFTW
Account-123
A V1.0……
Consumer
Upcaster
B V2.0
f: v1->v2
f: v2->v3
f: v3->v4
…
f: v97->v98
f: v98->v99
f: v99->v100
@koenighotze#Devoxx #EventSourcingFTW
Good luck maintaining that monster
@koenighotze#Devoxx #EventSourcingFTW
The new version must be
constructible from the previous
version. Otherwise it is a new event
@koenighotze#Devoxx #EventSourcingFTW
Prefer simple, text-based,
human readable events
@koenighotze#Devoxx #EventSourcingFTW
Fancy speak for JSON
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
And correctness?
@koenighotze#Devoxx #EventSourcingFTW
String-ly typed events
work really well
@koenighotze#Devoxx #EventSourcingFTW
Weak schema to the rescue
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
Schema as a description
not as a contract
@koenighotze#Devoxx #EventSourcingFTW
how do I handle large streams?
@koenighotze#Devoxx #EventSourcingFTW
How can you handle event data
over a long period of time?
@koenighotze#Devoxx #EventSourcingFTW
You don’t
@koenighotze#Devoxx #EventSourcingFTW
Just create a
snapshot of the
stream
@koenighotze#Devoxx #EventSourcingFTW
Year’s end procedure
@koenighotze#Devoxx #EventSourcingFTW
Year end – also known as an accounting reference
date – is the completion of an accounting period.
At this time, businesses need to carry out specific
procedures to close their books.
https://debitoor.com/dictionary/year-end
@koenighotze#Devoxx #EventSourcingFTW
https://debitoor.com/dictionary/year-end
Year end – also known as an accounting reference
date – is the completion of an accounting period.
At this time, businesses need to carry out specific
procedures to close their books.
@koenighotze#Devoxx #EventSourcingFTW
Copy-Transform
@koenighotze#Devoxx #EventSourcingFTW
a.k.a. eventsourcing
refactoring powertool
@koenighotze#Devoxx #EventSourcingFTW
AccountCreated
…
MoneyTransferred
…
. . .
2017
@koenighotze#Devoxx #EventSourcingFTW
AccountCreated
…
MoneyTransferred
…
. . .
Initialized
…
Deactivated
…
. . .
AccountCreated
…
MoneyTransferred
…
2017
2018
@koenighotze#Devoxx #EventSourcingFTW
Transactionledger
Microservice
Initialized
…
Deactivated
…
. . .
. . .2017 2018
Hic sunt
leones
@koenighotze#Devoxx #EventSourcingFTW
Same idea if you need to
remodel your domain!
@koenighotze#Devoxx #EventSourcingFTW
. . .
TransferredAndBooked
@koenighotze#Devoxx #EventSourcingFTW
. . .
Initialized
Transferred
Booked
TransferredAndBooked
Deactivated
. . .
TransferredAndBooked
@koenighotze#Devoxx #EventSourcingFTW
The devil is in the detail
@koenighotze
Dealing with errors
@koenighotze#Devoxx #EventSourcingFTW
MoneyTransferred
eventId: 231233
amount: 97 Euro
withdrawnAt: 2018-08-30T08:58:26.624
@koenighotze#Devoxx #EventSourcingFTW
Just update the
event in the
eventstore!
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
Challenges?
@koenighotze#Devoxx #EventSourcingFTW
Account-123
……
Consumer
…… …
@koenighotze#Devoxx #EventSourcingFTW
Consumer
Update …
 ¯_(ツ)_/¯
Account-123
…… …… …
I already
know that
event.
Why should I
re-read?
@koenighotze#Devoxx #EventSourcingFTW
Ok. Then just use
compensation
events
@koenighotze#Devoxx #EventSourcingFTW
The cancelled or corrected event
@koenighotze#Devoxx #EventSourcingFTW
Partial compensation?
@koenighotze#Devoxx #EventSourcingFTW
MoneyTransferAmountCorrected
eventId: 9
amount: 97 EUR
reasonEventId: 8
MoneyTransferred
eventId: 8
amount: 97 Euro
…
@koenighotze#Devoxx #EventSourcingFTW
Full compensation
do as accountants do
@koenighotze#Devoxx #EventSourcingFTW
MoneyTransferCancelled
eventId: 2
reasonEventId: 1
reason: …
MoneyTransferred
eventId: 1
amount: 97 Euro
…
MoneyTransferred
eventId: 3
amount: 97 EUR
…
@koenighotze#Devoxx #EventSourcingFTW
The full compensation makes the
reason for compensation explicit
@koenighotze
GDPR, compliance and
eventsourcing
@koenighotze#Devoxx #EventSourcingFTW
GDPR
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
Just encrypt and
throw the key away
@koenighotze#Devoxx #EventSourcingFTW
Every stream is encrypted
using a stream-specific key
@koenighotze#Devoxx #EventSourcingFTW
Stream
EventEvent Event …
PayloadPayload Payload
Stream
EventEvent Event …
PayloadPayload Payload
Stream
EventEvent Event …
PayloadPayload Payload
@koenighotze#Devoxx #EventSourcingFTW
“Please delete all my data”
@koenighotze#Devoxx #EventSourcingFTW
Deletion is effectively deleting
the stream-specific key
@koenighotze#Devoxx #EventSourcingFTW
Stream
EventEvent Event …
PayloadPayload Payload
Stream
EventEvent Event …
PayloadPayload Payload
Stream
EventEvent Event …
PayloadPayload Payload
?
@koenighotze#Devoxx #EventSourcingFTW
Challenges?
@koenighotze#Devoxx #EventSourcingFTW
Key administration
Finding what needs to be deleted
Storage implications
Coding complexity
Dashboards, Monitoring
@koenighotze#Devoxx #EventSourcingFTW
Being able to delete is awesome
@koenighotze#Devoxx #EventSourcingFTW
User
Bankaccount
Transaction
-ledger
@koenighotze#Devoxx #EventSourcingFTW
“Please delete all my data”
@koenighotze#Devoxx #EventSourcingFTW
Cascading deletes with tombstones
@koenighotze#Devoxx #EventSourcingFTW
User
Microservice
Bankaccount
Microservice
Transactionledger
Microservice
User-456
Deleted
Bank-
account-123
Deleted
@koenighotze#Devoxx #EventSourcingFTW
$et-$deleted
@koenighotze#Devoxx #EventSourcingFTW
User-456
… … …
Eventstore
delete
User-456
Deleted
emit
tombstone
event
User Microservice
deleteStream(‘User-456’)
@koenighotze#Devoxx #EventSourcingFTW
Bankaccount-123
AccountOpened
owner: user-456
…
Bankaccount
Microservice
User-456
Deleted
Eventstore
delete
Bank-
account-
123
Deleted
emit
tombstone event
deleteStream(‘Bankaccount-123’)
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
What if I delete a confluence account?
@koenighotze#Devoxx #EventSourcingFTW
Public/private data
@koenighotze#Devoxx #EventSourcingFTW
User—Public-456
Id Blue
User-Private-456
Schmitz David D-AW-123
@koenighotze#Devoxx #EventSourcingFTW
User—Public-456
Id Blue
User-Private-456
Schmitz David D-AW-123
Keep this
@koenighotze#Devoxx #EventSourcingFTW
User—Public-456
Id Blue
User-Private-456
Schmitz David D-AW-123Delete this
@koenighotze#Devoxx #EventSourcingFTW
You may be able to keep
references to the public data
@koenighotze#Devoxx #EventSourcingFTW
Just anonymise
the data
@koenighotze#Devoxx #EventSourcingFTW
Recital 26

EU GDPR
(26) The principles of data protection should apply to any information
concerning an identified or identifiable natural person.
Personal data which have undergone pseudonymisation, which could
be attributed to a natural person by the use of additional information
should be considered to be information on an identifiable natural
person.
@koenighotze#Devoxx #EventSourcingFTW
Recital 26

EU GDPR
(26) The principles of data protection should apply to any information
concerning an identified or identifiable natural person.
Personal data which have undergone pseudonymisation, which could
be attributed to a natural person by the use of additional information
should be considered to be information on an identifiable natural
person.
@koenighotze#Devoxx #EventSourcingFTW
Surprise: No easy answers
@koenighotze#Devoxx #EventSourcingFTW
Ask your lawyer or CISO
@koenighotze#Devoxx #EventSourcingFTW
That’s it?
@koenighotze#Devoxx #EventSourcingFTW
ES + DDD =
Needs more up-front design
You can refactor, you can clean up
Not enough in-depth books
Avoid frameworks esp. if going polyglot
Beware: “just…” or “…made easy”
@koenighotze#Devoxx #EventSourcingFTW
Forget this talk…read some papers
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
Choose the right tool?
@koenighotze#Devoxx #EventSourcingFTW
@koenighotze#Devoxx #EventSourcingFTW
Thank you!
Questions?
Comments? Blame?
@Koenighotze
AND VOTE
(not only
at Devoxx)

Event Sourcing - You are doing it wrong @ Devoxx