Applying DDD to DDD leaves no Domain-Driven Design
We developers have unnecessarily complicated DDD. It's really just about understanding the domain. A plea for more pragmatism.
(Image: sabthai/Shutterstock.com)
- Golo Roden
Domain-Driven Design (DDD) promises better software through a focus on the business domain and a shared understanding between developers and domain experts. That's the essence, condensed into one sentence. But if you actually apply this principle to DDD itself (i.e., asking what the domain is, what's important, what can be omitted, ...), you don't end up with what we call "DDD" today. You end up with something much simpler.
So why, after more than two decades, has DDD never left its niche? Why do so many developers feel overwhelmed, confused, or believe they aren't smart enough for it? The answer is uncomfortable, but clear: DDD fails due to its own ambition.
The Problem: DDD Scares People Away
DDD has been around since 2003. The book "Domain-Driven Design: Tackling Complexity in the Heart of Software" by Eric Evans is considered a classic, and the ideas within it are powerful. Yet, DDD remains confined to a relatively small community. It hasn't become mainstream. It hasn't revolutionized how most teams build software. And a significant reason for this is: we lose people before they've even grasped the core idea.
Instead of meeting developers where they are, we overwhelm them with a mountain of theory: Aggregates, Bounded Contexts, Value Objects, Entities, Repositories, Anti-Corruption Layers, Ubiquitous Language, Strategic Design, Tactical Design, and so on. The terminology alone is intimidating. The concepts are abstract. The examples often remain academic. And by the time someone has worked through all of this, they are either exhausted or convinced that DDD is only for enterprise architects with decades of experience.
This is no accident. DDD has been academicized. It's been turned into a framework, a methodology, a certification path. It's been cloaked in jargon and buried under layers of pattern catalogs. What began as a simple, human-centered idea ("understand the domain and speak the language of the business") has become something that feels out of reach for most.
And this is the paradox: DDD is supposed to make software development easier and better aligned with reality. But the way we teach it, the way we talk about it, the way we practice it: all of this makes it harder.
Videos by heise
Intention vs. Implementation: A Familiar Story
This isn't a new problem. The same thing happened with the design patterns from the "Gang of Four."
Take the Singleton pattern, for example. The intention is simple: you want to ensure that a class has exactly one instance. That's it. That's the problem the pattern solves. But what did most developers learn? The implementation: a private constructor and a static getInstance() method. They memorized the code. They applied it mechanically. And (too) many of them never understood that the implementation is just one way to achieve the intention. The implementation became the pattern itself.
The same has happened with DDD. The intention is clear: you want to build better software by focusing on the business domain and developing a shared language with domain experts. But what do developers learn? They learn the implementation: a long list of patterns and (technical) concepts. And just like with Singleton, many believe that applying these patterns *is* DDD. They treat it like a checklist.
"Do we have Aggregates? Check. Do we have Value Objects? Check. Do we have Bounded Contexts? Check."
But that's not DDD. That's pattern theater. It's form without substance. It's like holding daily stand-ups, sprints, and retrospectives and calling yourself agile, even though nothing has changed in the team's culture, communication, or decision-making. You perform the rituals, but you miss the point.
The Great Escape: Hiding Behind Patterns
Here's the uncomfortable truth: DDD is not technical at its core. What it truly demands of you is this: Step out of your comfort zone. Talk to domain experts. Not just to gather requirements, but to truly understand their world. Listen. Ask questions. Tolerate ambiguity. Admit when you don't understand something. Develop a shared language, iteratively and collaboratively – and above all, a shared understanding.
This is hard work. It's work with people. It's uncomfortable work. It requires humility, patience, and communication skills that many developers, myself included, haven't been trained in. It's much easier to stay in the technical realm, where things are predictable and controllable.
And here's the trap: the patterns offer you a perfect hiding place.
Instead of talking to people, you can debate whether something should be an Aggregate or an Entity. Instead of understanding the domain, you can draw Bounded Context diagrams. Instead of asking domain experts "What do you call this?", you can ask your colleagues "Is this a Value Object?". You feel productive. You create structure, draw diagrams, apply patterns. But the real work, the hard, human, domain-focused work, gets pushed aside.
This is why so many DDD projects look technically mature but feel disconnected from the business. The code is full of the right patterns, but it doesn't speak the language of the domain. The architecture is clean, but the developers still don't truly understand what the software is supposed to do or why.
"I'm Too Dumb for DDD"
Unfortunately, there's a very sad, often personal reason for this. Because the feeling that engaging with DDD leaves many with is insecurity. For years, I myself thought I was too dumb for DDD because I didn't feel like I truly understood it. Of course, I read the Blue Book. I watched talks. And the big picture made sense: focus on the domain, build a shared understanding! But I got lost in the details. Aggregates, Bounded Contexts, Anti-Corruption Layers. I understood them in theory, but I never felt confident applying them. I thought everyone else just "got it," and I didn't.
Then I realized: the problem wasn't me. The problem was the way DDD is taught.
DDD has been unnecessarily complicated. It's been dressed up in academic language, buried under layers of abstraction, and presented as if you need a Ph.D. in software architecture to understand it. But that's not what DDD is. DDD is not complicated. We complicated it.
If you've ever felt this way, if you've ever thought you weren't smart enough for DDD, or that it's only for certain types of projects, or that it's just too abstract to be useful, then I can assure you: you are not alone. And you are not the problem.
What DDD is Really About
Let's strip away the complexity and get to the essence. Here's DDD in one sentence:
Build better software by understanding the business domain and speaking a shared language with the people who work in it.
That's it. Everything else – all the patterns, all the diagrams, all the terminology – is secondary. The patterns are tools. They can help you express the domain more clearly in code. But they are not the goal. The goal is understanding.
So, what does this look like in practice?
First: Talk to people, not diagrams. The most important question you can ask isn't "Is this an Entity or a Value Object?", but rather "What do you call this?". Spend time with domain experts. Listen to how they describe their work. Pay attention to the words they use, the distinctions they make, the things that are important to them. This is where the shared language comes from. Not from UML diagrams, but from real conversations.
Second: Use the language of the domain, not the language of technology. If domain experts talk about customers, guests, and users as three different concepts, your code should reflect that. Don't lump them all into a single User class just because it's easier. Don't call something UserEntity when the business calls it Customer. The precision of language is important. This isn't pedantry; it's the core of DDD.
Third: Name things precisely. Is it "cancel" or "refund"? Is it an "invoice" or a "receipt"? Is it "return" or "revoke"? These aren't trivial distinctions. They represent different business processes, different rules, different meanings. If you choose the wrong names, you've already lost alignment with the domain.
Fourth: Away from CRUD. Create, Read, Update, Delete – these are technical operations, not business processes. They don't capture what's actually happening in the domain. Instead, think in verbs that come from the business: confirmOrder(), cancelOrder(), changeDeliveryAddress(). These names tell you what the system is doing and why.
Finally: Patterns are tools, not goals. If an Aggregate pattern helps you express a business concept more clearly, use it. If it doesn't, leave it out. Let the domain guide your structure, not pattern catalogs. The patterns exist to serve the domain, not the other way around.
Why Event Sourcing Feels Natural
In fact, there's something I've noticed over the years that can help enormously: people understand events intuitively.
Events are how we naturally think about the world. We tell stories as a sequence of things that happened. We describe our day in events: I woke up, I ate breakfast, I commuted to work, I was in a meeting. When we tell history, we talk about events: The wall fell, the treaty was signed, the discovery was made. This isn't a technical abstraction. It's how humans process and communicate information.
Imagine you're telling the story of Little Red Riding Hood. You wouldn't do it like this:
UPDATE LittleRedRidingHood SET location = 'Forest' UPDATE Wolf SET location = 'Forest' UPDATE LittleRedRidingHood SET emotion = 'scared' DELETE Grandmother UPDATE Wolf SET disguise = 'Grandmother'
That's absurd. It strips away all meaning, all causality, all narrative. You would tell it as a sequence of events:
LittleRedRidingHoodEnteredTheForest LittleRedRidingHoodMetTheWolf WolfWentToGrandmothersHouse WolfDevouredGrandmother WolfDisguisedHimselfAsGrandmother
This is how stories work. This is how we understand what happened and why. And this is exactly how Event Sourcing works.
Event Sourcing doesn't ask "What is the current state?", but rather "What happened?". It stores a chronological log of facts and occurrences in the past, immutable and undeniable. And from this log, you can reconstruct the current state, replay the history, understand causality, and build a complete picture of what led to where you are now.
Event Sourcing as a Lightweight Path to DDD
Event Sourcing isn't just a way to store data. It's also a surprisingly effective, lightweight entry point into the world of DDD because it naturally pushes you in the right direction.
First, it forces you to think in verbs, not CRUD. Events have names like OrderPlaced, PaymentReceived, or ItemShipped. Not createOrder, updatePayment, or deleteItem. You can't fall back on generic CRUD operations. You have to describe what actually happened in the business. This immediately brings you closer to the domain language.
Second: Organizing events by subject is half the aggregate discussion. Which events belong together? All events for a specific order end up under /orders/42. All events for a specific customer might end up under /customers/23. Or you structure it differently: /customers/23/orders/42. The way you group events reflects the structure of the domain. You do the work of defining aggregates, but you do it concretely by deciding which events belong together, not by debating abstract concepts.
Third: Deciding which service processes which events is the bounded context discussion. Should the order service process PaymentReceived, or should that be the billing service? What belongs to inventory versus fulfillment? These are bounded context questions. But you're not drawing diagrams. You're making concrete decisions about which part of your system is responsible for which events. The domain emerges naturally from these decisions.
Finally: Events are self-explanatory. You don't need to understand aggregates, entities, or value objects to know what CustomerRegistered means. You don't need a degree in DDD to figure out what BookBorrowed represents. Events speak the language of the business. They are accessible. They are concrete. They are immediately understandable.
Let's look at a simple example from a library system. Instead of thinking about whether a book is an entity or an aggregate, first ask: What can happen to a book?
BookAcquired: The library gets a new book.BookBorrowed: Someone borrows it.BookReturned: It's brought back.LateFeeCharged: It was returned late, and a fine was charged.BookRemoved: The library decommissions it.
These events tell the story. They capture the business process. And if you organize them by subject, say /books/42 for book number 42, you've just defined what would be called an aggregate in DDD terms. You didn't need the theory. You simply asked: What happens, and what belongs together?
A Plea for Pragmatic DDD
Perhaps we don't need *more* DDD. Perhaps we need more pragmatic DDD.
Strip away the academic language! Stop treating patterns as the goal! Stop overwhelming people with theory before they've even written a line of code! Come back to the essence: focus on the domain, speak the language of the business, build a shared understanding!
If you want to learn DDD, start here:
- Forget the patterns for now. Learn to have good conversations with domain experts.
- Learn to listen attentively, ask the right questions, and tolerate ambiguity.
- Learn to think in events: what happened, and why?
- Let the domain guide your code, not the other way around.
If you're already practicing DDD, ask yourself honestly:
- Am I hiding behind patterns instead of taking on the difficult work of understanding the domain?
- How much time do I spend with domain experts compared to how much time I spend drawing diagrams?
- Does my code speak the language of the business, or does it speak the language of technology?
- Am I using CRUD, or am I using events and domain verbs?
DDD was never meant to be complicated. We complicated it. If we take DDD seriously, if we apply its own principles to DDD itself, then we must admit: we have drifted from the core. We have built layers of abstraction that obscure the simplicity underneath.
Event Sourcing can help. It's concrete. It's intuitive. It aligns naturally with how people think. And it pushes you, almost effortlessly, towards the practices that DDD has always been about: understanding what happened, naming it precisely, and speaking the language of the domain.
If you're interested in Event Sourcing and want to see how it can lead you to better domain modeling without the overhead, start with an introduction to Event Sourcing. You might find that the path to DDD is simpler than you thought.
(rme)