The Manuscript Is Done. The Code Is Public.

In August I wrote that you should build the thing first and let the code tell you what the chapters say. That post was a bet made in the middle of the work. This one is the report.

The manuscript is finished: 452 pages, 18 chapters, 72 diagrams, 79 code listings. Not a draft I am still circling. Finished.

More useful to you: the reference implementation it was built on is now public. It lives at github.com/ThomasJaeger/event-sourcing-cqrs, MIT licensed, .NET 10 and C# 14, tagged v1.0.0, which is the release the book describes.

What is actually in the repository

It models an order-management domain across five bounded contexts, event-sourced end to end. Five aggregates rebuilt from events. Two process managers that are themselves event-sourced on their own streams, with compensation branches. Eight projections maintaining read models over a mix of relational tables and JSONB. Four hosts: a Blazor Server UI, a JSON API, a workers host carrying projections and the outbox, and an admin console. Role-based authorization and tenant isolation run through all of them.

Fifty-three architecture decision records carry the reasoning. If you only read one part of the repository, read those. They are where the arguments are, including the ones I lost.

It is licensed so you can lift pieces of it into commercial work without asking me.

The part that cost the most

Four event stores sit behind one interface as first-class peers: PostgreSQL hand-rolled over Npgsql, SQL Server hand-rolled over Microsoft.Data.SqlClient, KurrentDB over gRPC, and DynamoDB with conditional writes. All four pass the same contract suite. Switching between them is a configuration change.

That constraint was the most expensive decision in the project and the one I would make again. The moment a second store has to satisfy the same contract, every convenience that leaked out of the first one becomes visible. Optimistic concurrency stops being a paragraph and becomes four different mechanisms that have to produce the same guarantee. You cannot hand-wave a contract test suite.

It also answers the question I get most often, which is some version of “do I have to adopt a new database for this?” No. The domain model does not have to change when that answer changes.

Where the book stands

It is in submission. No publisher is named here, there is no publication date, and I am not going to invent one. When there is a contract to announce, I will announce it.

I am saying that plainly because the alternative is the thing I find tiresome in this corner of the industry: announcements engineered to imply more than they say. The manuscript being finished is a fact. The code being public is a fact. A publication date is not yet a fact.

What you can do with this today

Clone it. Run it. The README is honest about what docker compose up does and does not start, which is the kind of detail that usually costs a reader an afternoon.

Then tell me where it is wrong. Not where it is unfamiliar, where it is wrong. Open an issue. The book is finished, but the implementation continues on main past the tag, and a reference implementation that nobody argues with is not much of a reference.

One thing it will not do is convince you to event-source your whole system. A good chunk of the book is about where these patterns do not belong, and the honest answer for a lot of software is a boring table you update in place. Chapters that only sell the pattern are how teams end up with an event store full of StatusUpdated.


I run Legacy to Modern LLC, where I modernize MS-DOS, Visual Basic, Delphi and .NET applications. If your .NET application is on a current runtime but was designed as CRUD over tables, that is a specific problem with a specific path out.

Leave a comment