Skip to content

Perspectives

We'll Take the Benefits, Hold the Rules

There's a moment that shows up in Event Sourcing projects again and again. The design is going well, the team is sold on the idea, and then a single hard requirement surfaces: GDPR's right to erasure. All at once, storing events immutably looks less like a feature and more like a liability. So the team grants itself one small exception – when someone asks to be deleted, they'll reach into the stored event and overwrite the offending field, turning an email address into "redacted". One small edit, applied rarely, to a system that is otherwise append-only.

It sounds harmless. It even sounds responsible. But that one exception quietly cancels a large part of the reason they chose Event Sourcing in the first place, and they likely won't notice until the day they need one of the guarantees that no longer holds. Because the benefits of Event Sourcing aren't a menu you order from. They all hang on a single thread, and that field overwrite cuts it.

Thinking in Events

Most of us never learned Event Sourcing as a way of thinking. We learned it as a collection of parts. An event store goes here. A read model goes there. When things turn asynchronous, you reach for a saga, and when reads and writes start pulling in different directions, you reach for CQRS. We assemble the pieces the way the tutorials drew them, and for a good while the system holds together.

Then a question arrives that no tutorial covered. Should this be one stream or two? Does this failure belong in the log? Who owns this piece of state? The patterns go quiet, because we learned their shapes without the reasons that gave rise to them. Underneath the patterns, though, there is something smaller and steadier, and a while ago we tried to write it down: a short manifesto that everything else turns out to be a consequence of. Here it is, and here is what it means.

What's an Entity, Anyway?

I've worked with Domain-Driven Design for fifteen years, and I'll admit something I usually keep to myself. There's one concept in DDD whose point I've never quite gotten. Not because I haven't read Evans, and not because I haven't built systems around it. I've done both. But every time I sit with the concept of the Entity and ask myself what it's actually for, I come away with the same shrug. Value Objects feel intuitive. Aggregates feel intuitive. The Entity feels like a placeholder that everyone agreed to keep teaching anyway.

A conversation a few days ago finally produced the click I'd been waiting for, and the answer surprised me. The confusion wasn't a personal blind spot. The concept itself has been quietly hollowed out by the very ideas that surround it, and most of us keep teaching it as if that hadn't happened. The Entity isn't broken. It's overshadowed. And the shadow only lifts once you start asking what would happen if the thing casting it weren't there.

Is DDD Overkill for My CRUD Project?

We hear it almost every week. A developer leans back, half-smiles, and says something like: "Domain-Driven Design? Sure, for a big enterprise system. But mine is just a simple CRUD app. Wouldn't DDD be total overkill?" The tone is friendly, sometimes even a little apologetic, as if they're letting us down gently. And the reasoning sounds airtight. CRUD is simple. Domain-Driven Design is heavy. Why bring a freight train to move a couch?

We get the instinct. Most of us have rolled our eyes at a slide deck full of Bounded Contexts for an app that, frankly, has one table and three buttons. But once you scratch the surface of this question, two different things turn out to be hiding under the same word. One of them really is overkill for your CRUD app. The other one isn't even optional, and you're either doing it well or doing it badly right now, whether you call it DDD or not.

Continuous Modeling, or What Happens to the Model on Tuesday?

Friday afternoon. The sticky notes on the wall are the densest they've been all week. Someone has redrawn a Bounded Context for the third time, and this time everyone in the room nods. Two product managers, three engineers, a domain expert, and a coach who's been keeping the conversation honest agree: this is what the system actually is. Phones come out, photos get taken. People shake hands and say things like finally, and now we know. The room empties.

On Tuesday, someone opens a pull request that touches the boundary you spent half of Friday redrawing. The PR description doesn't mention the model. The reviewer doesn't mention the model. The model is in three Miro boards, one of which is now read-only because somebody's account got rotated, and a Confluence page that hasn't been opened in four weeks. The reviewer approves the change. The model isn't wrong yet. It's just not anywhere the work can see it. Three months later, when a new question forces you back to the wall, no one quite remembers why the boundary was where it was. Did the workshop fail? It didn't. Something else did, and almost every team we talk to keeps making the same quiet mistake.

How Can I Force Myself to Stop Thinking in Technical Solutions?

Henry Ford is often quoted as having said, "If I had asked people what they wanted, they would have said faster horses." The line is probably apocryphal, but the picture it paints is accurate. People describe solutions, not problems. They say "I need a queue" when what they mean is "things keep getting lost between two systems." They say "we need a microservice" when what they mean is "this part of the codebase is painful to deploy." The technical answer arrives before the question is even formed, and that is true for product owners, business stakeholders, and developers alike.

This is the trap every thoughtful developer eventually wants to escape. After a few years, you start to notice that the systems you build are answering the wrong questions. You read about Domain-Driven Design, you learn the words "ubiquitous language" and "bounded context", and you decide that from now on, you'll think about the business first and the technology second. Then you walk into your next meeting, and within ninety seconds you're already drawing tables on the whiteboard. You wanted to stop. You couldn't. This post is about why, and about the only thing we've actually seen work.

Ubiquitous, But in Which Language?

A few years ago, we sat in a workshop room in Switzerland with a team that wanted to build an event-based dispatching system for their industrial operations. The stakeholders had flown in from across the country, from the German-speaking north, the French-speaking west, and the Italian-speaking south. We had everything Domain-Driven Design tells you to want, the right people in the room, real domain expertise, real business problems to solve. What we didn't have was a shared mother tongue.

Domain-Driven Design rests on the idea of a Ubiquitous Language, a precise vocabulary that every member of a project uses identically, in every conversation, every document, and every line of code. It's beautiful in theory. But it quietly assumes there is one language to be precise in. In a surprising number of real projects, there isn't. What we did in that Swiss workshop, what another client of ours did in an even more radical way, and why this matters more in Event-Sourced systems than almost anywhere else, is what this post is about.

Two Maps, One Domain

Most teams who say they "do Domain-Driven Design" are doing tactical DDD. Aggregates, Entities, Value Objects, Repositories. They write code that follows the patterns, draw diagrams that show the building blocks, and argue about whether something belongs in the domain layer or the application layer. It feels like DDD. It looks like DDD. And it is, in a narrow sense. But it's only one half of the picture. The other half, the strategic half, is where most teams quietly drift, often without noticing.

Strategic Design is the part that gets learned last. It's also the part that quietly determines whether your architecture aligns with the business or fights it. Inside Strategic Design lives a distinction that, once you see it, you can't unsee: the difference between the problem space and the solution space, and how Subdomains and Bounded Contexts inhabit them. Most teams either don't make this distinction at all, or they collapse it into a 1:1 mapping that feels tidy and turns out to be wrong. Let's pull it apart.

Great Minds Should Not Think Alike, They Should Think Together

The Event Sourcing, Domain-Driven Design, and CQRS community is full of brilliant people. Thoughtful practitioners, passionate speakers, prolific authors. People who genuinely care about building better software through deeper domain understanding. And yet, for a community built around the idea of shared understanding, we have a remarkably hard time understanding each other.

The irony is hard to miss. We preach Ubiquitous Language as a cornerstone of Domain-Driven Design, insisting that teams must develop a precise, shared vocabulary for their domain. But when it comes to our own discipline, we can't even agree on what to call the things we work with every day. And that's just the beginning.

REST in Peace

REST has become the de-facto standard for building web APIs. Almost every tutorial, framework, and job listing treats it as the obvious choice. But what most developers call "REST" has very little to do with what Roy Fielding described in his dissertation back in 2000. Today, REST typically means HTTP verbs, JSON payloads, and CRUD operations mapped to resources. And that is exactly where the problem begins.

Because once you strip away the buzzword, what remains is a thin wrapper around database operations, exposed over HTTP. We have spent years criticizing CRUD on the data level. It is time to look at what CRUD does to our APIs.