Skip to content
Leo Jung
Go back

[DDD Intro #1] DDD is not a technology, but a perspective

Edit page

When you first study DDD, technical terms naturally stand out first. Entity, Value Object, Aggregate, Repository, Domain Service, Domain Event. Go a little further and terms like Layered Architecture, Hexagonal Architecture, CQRS, and Event Sourcing follow.

That makes DDD easy to see as “a complex object-oriented design technique” or “a way to split microservices well.” In practice, many teams say they are adopting DDD and start by dividing packages into domain, application, and infrastructure, then giving every class a plausible name.

But if you start that way, DDD quickly becomes a burdensome list of techniques. You end up debating what should be an Entity, what should become a Value Object, and how much responsibility an Aggregate Root should hold. The more important questions get pushed aside.

What problem are we solving? What rules truly matter in this domain? Do domain experts and code use the same words? Is the same word being used with different meanings in different contexts?

The starting point of DDD is not a pattern. The starting point of DDD is a perspective on the domain.

Why code drifts away from the domain

Software often starts simply. Requirements seem clear, and the data structure seems straightforward. Suppose we manage a user’s subscription status in a subscription service.

At first, there are only a few states.

ACTIVE
CANCELED
EXPIRED

This looks like simple CRUD. A controller receives the request, a service changes the status, and a repository saves it.

subscription.setStatus(SubscriptionStatus.CANCELED);

The problem starts over time.

The operations team starts distinguishing “cancellation” from “refund.” The payment team says subscriptions that have already been charged must be handled differently from those that have not. Customer support says they need to distinguish user-initiated cancellation from admin-initiated cancellation. Marketing wants to analyze users who cancel during a free trial differently from users who cancel during a paid subscription.

Now “cancel a subscription” is no longer a simple status change.

Cancellation during a free trial
Cancellation right after recurring payment
Refundable cancellation
Non-refundable cancellation
Admin-initiated termination
Automatic suspension due to payment failure
Expiration after the usage period ends

The domain may have been complex from the start, or we may simply have learned its complexity over time. The important point is this: if the code cannot capture these differences, the complexity does not disappear. It scatters elsewhere.

Status values increase, service methods get longer, and conditions become nested. At some point the code says this:

if (subscription.getStatus() == ACTIVE &&
    subscription.getPaymentStatus() == PAID &&
    subscription.isRefundable() &&
    !subscription.isTrial()) {
    // ...
}

But the domain expert says this:

If there is no usage history within seven days after recurring payment, the user can receive a full refund.

The two sentences may refer to the same fact. But there is distance between the language of the code and the language of the domain. DDD is an attempt to reduce that distance.

DDD preserves domain knowledge in code

DDD is interesting not only because it provides good design patterns. DDD tells us to place domain knowledge at the center of software.

Software exists to solve some problem in reality. A banking system deals with financial domains such as accounts, transfers, limits, settlement, and risk management. An ecommerce system deals with products, orders, payments, delivery, returns, and settlement. A subscription service deals with plans, subscriptions, renewals, cancellations, refunds, and entitlements.

What matters here is not just data. The number of database tables matters too, but that is not the center of domain understanding. What matters more is which concepts have which meanings in the domain, which rules change them, and within which boundaries consistency must be preserved.

The model in DDD is a means of putting this domain knowledge into software. A model does not end as a diagram or a document. A model must live in code.

Compare these two snippets.

subscription.setStatus(SubscriptionStatus.CANCELED);
subscription.cancelBySubscriber(reason);

Both may ultimately change the status to CANCELED. But they express very different attitudes.

The first code modifies data. The second code expresses a domain behavior. Someone reading the first only learns that the status changed to canceled. Someone reading the second learns the domain meaning: the subscriber canceled it directly.

DDD cares about this difference. Meaningful domain behaviors and rules should be visible in code names, structure, and responsibilities.

The questions must change before the technology

Adopting DDD does not mean the architecture must change immediately. Changing package structures, creating repository interfaces, and wrapping every value in a Value Object do not automatically make a system DDD.

The most important sign that DDD has begun is not a code structure. It is a change in questions.

Before, we asked:

Which table should this API update?
Which fields does this screen need?
Should this logic go in a Service or a Util?

Once we start thinking in DDD, the questions shift:

What does this behavior mean in the domain?
Which object should be responsible for this rule?
Is this word used with the same meaning by every team?
What consistency must this change not break?
Within which boundary is this model valid?

When questions change, design changes. When design changes, the shape of code changes too. But the order matters. DDD does not start from the shape of the code. It starts from questions about the domain.

A domain expert is not a requirements messenger

One phrase that appears often in DDD is domain expert. But it is not enough to understand a domain expert as simply “the person who tells us requirements.”

A domain expert is not someone who delivers a feature list. A domain expert is someone who helps discover the language and rules of the domain. Developers are not people who merely transcribe their words. Developers must ask questions.

“Are cancellation and refund the same thing?”

“Do we also call a subscription stopped by payment failure a cancellation?”

“From a policy perspective, is automatic expiration after a free trial the same as user-initiated cancellation?”

“When customer support says recovery, does that mean reviving the previous subscription or creating a new one?”

Through these questions, domain knowledge becomes sharper. Words that first looked obvious turn out to be ambiguous. Something that seemed sufficient as a single status value may split into several domain events.

DDD places this process of knowledge discovery at the center of design.

DDD does not remove complexity

If expectations are wrong, DDD can be disappointing. DDD does not magically turn a complex domain into simple code. In fact, it exposes complexity that had been hidden.

This is important.

Making a complex domain look simple is not always good design. If domain rules are scattered behind code that looks simple, the complexity has not disappeared. It has become harder to find.

Good DDD reveals complexity honestly. But it does not let complexity scatter everywhere. Important rules move into domain objects, meaningful changes are expressed as domain events, and different models are separated by Bounded Contexts. DDD does not eliminate complexity. It names it, draws boundaries around it, and assigns responsibility.

That is why DDD can be excessive for simple systems. A deep domain model may not be necessary for an admin screen that stores a notice title and body. But when there are many policies, important state transitions, words that mean different things in different contexts, and a domain that will change over the long term, the DDD perspective becomes powerful.

The order for studying DDD

When first studying DDD, it is tempting to start with tactical patterns. Entity and Value Object are visible and easy to practice in code. But to understand DDD properly, it is better to start a little earlier.

First, understand the domain. Then examine the language that describes the domain. Find the boundary where the same language has the same meaning. Only after that can you understand why patterns such as Aggregate, Repository, and Domain Event are needed.

Patterns are not the answer. They are the result.

If you memorize Aggregate first, you get trapped in the question, “How much should I group together?” But if you ask, “Which changes must remain consistent together?” the need for an Aggregate becomes visible.

If you memorize Domain Event first, you get trapped in the technical question, “Should I publish an event?” But if you ask, “Did something important happen in the domain that another concern needs to know about?” the meaning of an event becomes visible.

If you memorize Bounded Context first, you get trapped in the question, “How should I split services?” But if you ask, “Within which boundary does this word have the same meaning?” the boundary of the model becomes visible.

Closing

DDD is not a technology. Of course, implementing DDD requires technology. Object-oriented design, architecture, and data storage all matter. But they are not the center of DDD.

The domain is at the center of DDD. More precisely, the attitude of trying to understand the domain is at the center. It is the attitude of building a shared language with domain experts, reflecting that language in code, finding the boundaries where a model is valid, and preserving important rules inside the code.

That is why DDD is more of a perspective than a technology.

Doing DDD is not about naming things Entity, Value Object, or Repository. It is about changing the questions so we can understand the domain better, then creating a structure so that understanding does not disappear from the code.

This series starts from that perspective. In the next post, we will look at what “model” means in DDD. A model is not a copy of reality. A model is the result of selectively interpreting reality for the problem we are trying to solve.


Edit page
Share this post:

Previous Post
[DDD Intro #2] A model is not a copy of reality