Skip to content
Leo Jung
Go back

[DDD Intro #8] Core Domain: Do not spend the same care everywhere

Edit page

When studying DDD, it is tempting to apply good design everywhere. We create Entities, Value Objects, split Aggregates, and set up Repositories and Domain Services. We publish Domain Events and cleanly separate layers.

But in real projects, this approach quickly becomes exhausting. Even a simple admin screen creates many classes. It feels like a notice registration feature needs an Aggregate, and changing the order of banners needs a Domain Event. Over time, people on the team start saying:

DDD is too complicated.

But what DDD really says is not that all code should become complicated. It is closer to the opposite. DDD says to handle complex domains honestly, but not to spend the same level of design energy on every area.

The concept needed here is Core Domain.

Not every domain has the same value

Several kinds of domains are mixed inside software. Some areas create business competitiveness. Some areas are necessary but are not the core of that competitiveness. Others are generic problems that most companies solve in similar ways.

Think of a subscription service.

If the core competitiveness of this service is “recommending the most appropriate plan based on a user’s usage pattern, and handling renewals and changes flexibly,” then subscription and pricing policy are very important domains. Rules such as when to offer a free trial, how to calculate the remaining period when switching from monthly to annual subscription, and how many days to keep service access after payment failure directly affect the service experience and revenue.

On the other hand, email delivery may be necessary but not a core competitive advantage. Authentication is important too, but in many cases we can use a proven solution or common pattern. Admin notice management is necessary, but it may not need a deep domain model.

It is true that every feature matters. But not every feature matters in the same way.

Core Domain, Supporting Subdomain, Generic Subdomain

In DDD, we can roughly divide domains into three categories.

Core Domain
Supporting Subdomain
Generic Subdomain

Core Domain is the area that becomes the core competitiveness of the business. This is where we need the deepest understanding and the best design energy. We must talk with domain experts for a long time, and we must keep refining the language of the model.

Supporting Subdomain is an area that supports the core domain. It is necessary for the business, but it is not the center of competitive advantage. In a subscription service, notifications, coupons, customer inquiries, and similar areas may belong here. This does not mean they can be built carelessly. It only means they do not always require modeling as deep and precise as the Core Domain.

Generic Subdomain is a common area that appears in many systems. Authentication, email delivery, file upload, and payment gateway integration may belong here. In these areas, using proven libraries, external services, or general-purpose solutions may be better than building them ourselves.

For a subscription service, it could be summarized like this:

Core Domain
- Subscription state management
- Plan change policy
- Renewal and expiration
- Access entitlement calculation
- Free trial and conversion policy

Supporting Subdomain
- Coupon issuance and use
- Notification delivery scenarios
- Customer inquiry handling
- Operator management features

Generic Subdomain
- Member authentication
- Email delivery
- Payment gateway integration
- File storage

This distinction is not a fixed answer. In some companies, payment itself may be the Core Domain. In others, notifications may be the core experience. What matters is asking what sits at the center of competitiveness in our business.

DDD is a technique for focus

Teams that first apply DDD often try to turn everything into a domain model. But this wastes design energy.

Suppose there is a notice feature that simply stores a title and body. If the feature has no complex state changes and no important business policy, a structure like this may be enough:

NoticeController
NoticeService
NoticeRepository
NoticeEntity

There may be no need to create structures such as NoticeAggregate, NoticeCreatedEvent, NoticeFactory, or NoticePolicy. Code like that is not DDD. It is an excessive structure that only looks like DDD.

Subscription renewal policy should be treated differently. If rules such as retry intervals after payment failure, grace periods, whether to keep access, expiration handling, notification delivery, and conflicts with plan changes are intertwined, this area should be modeled deeply.

subscription.renew(paymentResult, renewalPolicy, now);

This one line contains domain rules, not just a simple state change. If these rules determine service revenue and customer experience, spending good design here is not a cost. It is an investment.

Do not make important areas ordinary

Applying DDD everywhere is a problem, but treating the Core Domain like simple CRUD is also a problem.

Suppose we manage subscription state only with ACTIVE, CANCELED, and EXPIRED. At first, this may seem sufficient. But over time, requirements like these arrive:

A user in a free trial can still use the service until the expiration date after canceling.
After payment failure, service access stays active for three days.
When switching to an annual subscription, the remaining monthly subscription amount is deducted.
An enterprise plan can be renewed only when the minimum number of seats is satisfied.
A refunded subscription cannot be reactivated.

If these rules scatter everywhere, the Core Domain gradually becomes hard to understand. Conditionals increase in Application Services, Repositories gain queries that change state directly, and the frontend starts interpreting ambiguous server state values.

When we treat the Core Domain as ordinary CRUD, complexity does not disappear. It scatters into conditionals, exception handling, operation manuals, and people’s memory.

This is exactly where DDD is needed.

Design level should match domain value

Good design does not have the same shape everywhere. Good design has a level appropriate to the value and complexity of the problem.

The Core Domain needs deep conversation with domain experts. Model names must be chosen carefully, invariants must be found, and Aggregate boundaries must be considered. Tests should also explain domain rules.

For Supporting Subdomains, apply as much modeling as needed. It is enough if they do not disturb the core domain and remain maintainable.

For Generic Subdomains, using already proven solutions may be the better choice. Sometimes buying well is better than building well.

This distinction is important for protecting the team’s energy. To sustain DDD for a long time, we also need to know where not to use it.

Core Domain can keep changing

There is one caution. Core Domain is not fixed forever once it is chosen. When business strategy changes, the core domain can change too.

At first, subscription payment and renewal may be the core, but later personalized recommendations may become the core. At first, coupons may be a simple supporting feature, but later promotion strategy may become central to service growth. Conversely, a feature that was once core may be replaced by an external solution.

So finding the Core Domain does not end with one analysis. As the product grows and the market changes, we must keep asking again:

Where does our service's competitiveness come from now?
Which rules are changing often?
Which features directly affect business outcomes?
Which area becomes the most painful if designed poorly?

The answers to these questions decide design priorities.

Closing

DDD is not a way to make every piece of code look advanced. DDD is a way to ask where design energy should be focused in order to handle a complex domain.

We do not need Aggregates everywhere. Not every state change needs a Domain Event. Not every feature must be wrapped in Hexagonal Architecture. What matters is finding which domain in our system we truly need to understand deeply.

Identifying the Core Domain means deciding, “We should ask more questions here.” Distinguishing a Generic Subdomain means deciding, “We should not obsess too much here.”

DDD is a technique for focus. When we distinguish where to go deep and where to allow simplicity, DDD becomes a more realistic design approach.


Edit page
Share this post:

Previous Post
[DDD Intro #9] Context Map: Boundaries do not exist alone
Next Post
[DDD Intro #7] Why DDD fails