Skip to content

What's New

Welcome to the official EventSourcingDB blog – your source for announcements, articles, and insights. Here you'll find updates about product releases and improvements, feature announcements, background articles, and practical guidance for working with EventSourcingDB.

Whether you're exploring event sourcing, following the latest changes, or looking for deeper technical insights, this blog keeps you informed.

There Is No Row to Lock

Optimistic locking is one of those techniques you learn once and then use everywhere. A version column, an UPDATE ... WHERE version = 7, and whoever arrives second finds zero rows affected and has to try again. It is cheap, it needs no coordination, and it has kept relational systems correct for decades.

Then you move to Event Sourcing, and the technique has nothing to attach itself to. There is no row that represents the current state, so there is no row to lock. What you would need instead is a condition over a set of records that a query describes – and that turns out to be the one thing a relational database has no single statement for.

Not an Event Yet

Every event stored in EventSourcingDB is a CloudEvent. What your application sends in order to write one is not. It is missing seven of the eleven fields a stored event carries, and if you try to supply them yourself, the database rejects the whole request.

That gap has a name. Internally we call it an event candidate – not an event yet, more like an application to become one. From the outside this looks like pedantry. It is the reason everything else can be trusted. So let's look at what a candidate brings, what the server adds, and why we closed nearly every freedom the CloudEvents standard was generous enough to leave open.

Start With One Table

You have been circling Event Sourcing for a while now: long enough to be convinced by the idea, not long enough to have built anything with it. Today you finally stopped reading and picked one table to start with, the one holding who pays you for which plan. Which is when a colleague asks you why. The table works. The reports run. Nobody has complained. And when you are done, it holds the same values it held that morning, so from the outside, you spent an afternoon achieving nothing.

Eighteen months later, your product owner turns up with a question nobody could have answered before, and you have the answer in twenty minutes, because of what you did that afternoon. This post is both halves of that: first the work that looks like wasted time, in full, with the code, and then what it was for. Everything in it is invented, from the company to the customers to the numbers, but the code is real, and it ran exactly as shown.

PostgreSQL Is Not an Event Store

The table takes about fifteen minutes to write. An ID, a stream name, a version number, an event type, a JSON payload, a timestamp. Add a unique constraint, add an index, and you have somewhere to put your events. It is the first thing almost every team reaches for, and for good reason: the database is already running, already backed up, already monitored, and everybody on the team can read it.

In the previous post we pinned down what an event store actually has to do and held Kafka against that list. This one turns it around and points it at the tool people reach for from the opposite direction – the general-purpose database they already trust with everything else. PostgreSQL passes more of that list than Kafka did, and that is exactly what makes it the harder case. Kafka's central failure announces itself. PostgreSQL fails without a sound, a year in, when a read model is missing a row that nobody can account for.

Your Progress Bar Doesn't Belong in the Event Store

The progress bar works. It fills up, users can see how far along their contract review is, and nobody has complained. Then you open the event store and find 312 events for a single contract, 310 of which nobody would have written if that bar did not have to move.

Nothing is broken, and that is exactly what makes this hard to argue about. The system does what it should, the event names speak the language of the domain, and every one of those events was written on purpose. The unease is real anyway, and if you can't say where it comes from, the discussion ends before it starts. So: most of those events have a shelf life, and the event store is the one place in your system that doesn't.

Compensating Events Won't Save You

A customer calls about their account statement. One line records a payment of 4,800 euros, and nobody at your company can explain where it came from. You look into the event store, and there it is: payment-received, timestamped, hash-chained, immutable. The event is wrong, and it will be wrong forever.

Ask around and you will hear the same answer everywhere: write a compensating event – a new event that reverses the effect of an earlier one, the way a refund reverses a payment. It is good advice, and for two of the three situations that produce a wrong event, it is exactly right. For the third, it makes things worse – and the third is the one nobody warns you about.

Someone Built a Visualizer for ESDM – And It Wasn't Us

In mid-July, we put ESDM under the MIT license and wrote down a sentence that we meant as a principle: because the schema is public, tooling built on ESDM never needs us in the loop. Two weeks later, that sentence turned out to be a prediction instead.

Impierce Technologies, a company in the Netherlands and good friends of ours, built the ESDM Visualizer: an open source web application that turns a folder full of .esdm.yaml files into a board you can walk through, click into, and share as a link. Nobody asked us, and nobody had to. Here is what it is, what it does, and how to have it running against your own model in about a minute.

Until Further Notice

The printout lies on the table between us with one line marked in red. 31,400 euros, approved by N. Prentice, March 27, 14:52.

The N stands for Naomi. It is my line, and every number on it is correct.

Merrick asks whether I was authorized to do that. I say yes. He asks whether I can prove it.

Candy for AI: An Interview on Spec-Driven Development

Golo: Martin, you're the founder of Nebulit and the author of Understanding Eventsourcing, a book that deliberately refuses to treat Event Modeling, Event Sourcing, and Vertical Slice Architecture as three separate topics, and instead weaves them into a single, coherent way of building software. You also co-host the Event Modeling and Event Sourcing podcast, so you spend a lot of your time thinking out loud about this space. Over the last year, though, your attention has visibly shifted toward something new: getting AI agents to turn an event model into running, production-grade code, with as little human support as possible. Before we open the hood, take us back to the moment this clicked for you. What made you point your attention at autonomous code generation, and what convinced you it was more than the usual AI hype?

You Don't Need an Audit Log

Sooner or later, every serious system meets an auditor. Compliance, regulation, or plain good governance shows up and asks for the same thing: a complete, ordered, tamper-proof record of what happened and when. And almost every architecture review answers with the same reflex. Add an audit log. It has the right name, it produces something you can point at, and it feels responsible. In most systems, it is also the wrong answer.

This is not a case against auditing. Auditing matters, often more than the features built around it. It is a case against the separate audit log as the way to deliver it, because a good auditor, one who actually does the job, will take about one meeting to see through it. The reason they can is the same reason the audit log was compromised from the start, and it reaches all the way down to how you decided to store your data in the first place.