rosneri / writing
all posts
Engineering Sep 2025 · 12 min read

DDD for people who actually ship

Neri Rosner
Neri Rosner
backend-oriented full-stack engineer

Domain-driven design has a reputation problem: a 500-page book, a vocabulary that sounds like a cult, and a thousand blog posts arguing about whether a thing is an entity or a value object. I use DDD every week, and I use maybe three of its ideas. Here are the ones that pay rent, and the rest I happily ignore.

The trick is to treat DDD as a toolbox, not a religion. You don’t owe the book a complete implementation. You owe your future self code that’s easy to change.

Idea one: the language is the design

The single most valuable thing in DDD is the cheapest: name things the way the business names them, everywhere. If the domain expert says “payout” and the code says transferRecord, every conversation now needs a translation layer, and translation layers are where bugs hide.

A ubiquitous language isn’t ceremony — it’s the thing that makes a code review with a non-engineer possible. When the function is called reverseDirectTransfer and that’s exactly what the ops person calls it, you’ve removed an entire class of “we built the wrong thing” mistakes. This costs nothing and pays out forever.

Idea two: boundaries, and what’s inside them

The second idea is the bounded context — a part of the system with its own model and its own language, with deliberate seams to the rest. “Customer” means something different to billing than it does to support, and pretending it’s one shared model is how you get a 40-field god object nobody dares touch.

Inside a boundary, the aggregate is the unit that has to stay consistent together. Don’t overthink it: an aggregate is “the set of things that must be valid as a group, behind one entrance.” You change it through that entrance, you validate at that entrance, and you don’t let outside code reach in and mutate its insides. Most consistency bugs are just code reaching around the front door.

Idea three: keep the domain ignorant

The third idea — ports and adapters, hexagonal, whatever you call it — is that the domain shouldn’t know how it’s stored, transported, or triggered. The use-case logic talks to interfaces (ports); the database, the queue, the payment provider are adapters that implement them.

This sounds academic until the day the provider changes, or you want to unit-test the business rule without standing up Postgres. Then it’s the difference between a five-line test and a Docker Compose file.

// the domain depends on a port, not on Stripe or Postgres
interface PayoutPort {
  send(flow: FundFlow): Promise<Result<PayoutId>>;
}

// the use-case is pure logic + ports — trivially testable
async function processPayout(merchant: Merchant, deps: { payouts: PayoutPort }) {
  if (!merchant.eligible) return Err("ineligible");
  return deps.payouts.send(buildFlow(merchant));
}

The test passes a fake PayoutPort. Production passes the Stripe adapter. The use-case never knows the difference, and that ignorance is the whole point.

What I ignore

Most of the book, honestly:

DDD is worth it for exactly the reason most of it isn’t: the ideas that make code legible are cheap, and the ceremony that makes it “correct” is expensive. Take the first, skip the second.

Making it cheap enough to do every time

The reason teams abandon DDD is friction: if every new library is a manual layering exercise, people stop bothering by sprint three. So I made it free — a code generator that scaffolds a new backend library already partitioned into domain / application / infrastructure, with the ports and the test harness in place. Consistency by construction beats consistency by discipline, every time, because discipline runs out and constructors don’t.

That’s the whole philosophy: take the three ideas that make change easy — shared language, real boundaries, an ignorant domain — make them cheap enough to apply without thinking, and let the rest of the book stay on the shelf.


Filed under engineering. Think I’m skipping the wrong third? Argue with me.

See it in anger: the dead-letter queue is DDD applied to money movement. More writing
Keep reading Zero any, one year on 8 min Designing a dead-letter queue you can trust 11 min