Skip to content
Leo Jung
Go back

[DDD Intro #24] Event Sourcing is not required for DDD

Edit page

After learning Domain Event, we naturally encounter Event Sourcing. If we express things that happened in the domain as events, storing events instead of state may look better.

But using Domain Events does not mean we must use Event Sourcing. The two concepts are related, but they are not the same concept.

Domain Event is an object that expresses that something meaningful happened in the domain. Event Sourcing is a storage approach that stores the flow of events that created state, instead of storing the current state.

If we do not distinguish this difference, DDD becomes more complicated than necessary.

The common state storage approach

Most systems store the current state. For subscriptions, the database stores the current subscription state.

subscription_id: sub_123
status: ACTIVE
plan_id: PRO
started_at: 2026-07-01
expired_at: 2026-08-01

When the subscription is canceled, the state changes.

status: CANCELED
canceled_at: 2026-07-10

This approach is easy to understand. It is easy to query the current state and fits most CRUD systems well.

We can still use Domain Events while keeping state storage.

subscription.cancelByMember(reason, now);
subscriptionRepository.save(subscription);
eventPublisher.publish(new SubscriptionCanceled(subscription.id(), reason, now));

Here, the event is published for follow-up processing. But the basis of storage is still the current state.

What is different about Event Sourcing?

In Event Sourcing, we do not directly store the current state. Instead, we store the events that changed the state.

SubscriptionStarted
PlanChanged
SubscriptionRenewed
PaymentFailed
GracePeriodStarted
SubscriptionCanceled

When the current state is needed, we replay events from the beginning to rebuild it.

Subscription subscription = Subscription.replay(events);

In other words, the real storage target in Event Sourcing is not state, but a record of events. The current state is a result calculated from that record.

This approach preserves the process of domain change very richly. It tells not only that “the current subscription is canceled,” but also when it started, which plan changes it went through, what payment failures occurred, and why it was canceled.

Benefit: Time remains

The greatest benefit of Event Sourcing is that the flow of time is not lost.

In common state storage, only the current state remains. Of course, we can create history tables separately, but history is often treated as an additional record. In Event Sourcing, history itself is the source data.

This approach is powerful in situations like these:

Audit tracing is important.
We need to explain why state became what it is.
We need to restore state at a specific point in the past.
The flow of domain events itself has business value.
We want to create several read models based on events.

It can be attractive in domains where the reason and order of state changes matter, such as finance, accounting, order processing, complex workflows, and collaboration systems.

For example, simply knowing that a subscription state is CANCELED may not be enough. If we need to know whether the customer canceled it directly, whether it ended due to payment failure, whether an administrator terminated it, or whether it ended after a refund, the event flow has great value.

Cost: Complexity is high

Event Sourcing is powerful, but costly. It is not enough to simply create an event table.

First, event schemas must be managed. Events remain over time. Even when code changes, past events must still be readable. Changing event fields or names is not easy.

Second, state replay logic is needed. Aggregates must be able to restore their state by applying events.

subscription.apply(event);

Third, separate read models are often needed. An event store is not optimized for current screen queries.

Fourth, operations and debugging become difficult. Duplicate events, ordering issues, reprocessing, snapshots, and performance optimization appear.

Fifth, team learning cost is high. Event Sourcing is not simply a storage change. It is closer to a change in thinking.

Therefore Event Sourcing should not be introduced just because it looks attractive.

Distinguishing Domain Event and Event Sourcing

Again, Domain Event and Event Sourcing are different.

Domain Event can be used like this:

subscription.renew(paymentResult, now);
subscriptionRepository.save(subscription);
eventPublisher.publish(new SubscriptionRenewed(subscription.id(), now));

In this case, the current state of the subscription is stored in a normal table. The event is used for follow-up processing such as notifications, granting entitlements, and settlement reflection.

In Event Sourcing, the following is the source storage:

SubscriptionRenewed
SubscriptionCanceled
PlanChanged

There is no state table, or if one exists, it is a read model created from events.

Many systems are fine with only Domain Events. Even without storing every state as events, we can make domain events explicit, reduce coupling between contexts, and separate follow-up processing.

When should Event Sourcing be considered?

Event Sourcing can be considered when these conditions are strong:

The history of state changes is as important as the current state.
Audit tracing or legal proof is important.
Restoring state at a point in the past is needed.
It is valuable to create several Projections based on event flow.
Domain events are a core language of the business.
The team can handle operational complexity.

Conversely, it can be excessive in cases like these:

Most needs are current state queries.
History is sufficiently handled as a simple audit log.
Domain rules are relatively simple.
The team has no experience with event schema evolution and reprocessing.
There is not enough reason to bear operational complexity.

Even in a subscription service, Event Sourcing is not necessarily required. Even if subscription history matters, a combination of normal state storage, history tables, and Domain Events may be enough.

What matters more than event storage

Before introducing Event Sourcing, there is something to do first: finding proper names for domain events.

SubscriptionRenewed
PaymentFailed
GracePeriodStarted
SubscriptionExpired
PlanChanged
SubscriptionCanceled

We need to check whether these events are meaningful events in the domain and whether they are explained in the same language as domain experts use. If event names are closer to technical events, Event Sourcing will not create a good model.

SubscriptionUpdated
StatusChanged
DataSynced

These events are too generic. The value of Event Sourcing also decreases. What matters is not the technology of storing events, but modeling domain events well.

Closing

Event Sourcing is an attractive pattern. It preserves the full history of state changes, replays the past, and can create various read models. But it is also complex and has high operational cost.

Using Domain Events does not mean we must use Event Sourcing. In many systems, storing current state and publishing only important domain events is enough.

The purpose of DDD is not to create an event store. It is to understand and express meaningful changes in the domain. Event Sourcing is only a powerful option that makes those expressions the source of storage.

First, find domain events well. If the flow of those events itself has great business value and there is a reason to bear operational cost, it is not too late to consider Event Sourcing.


Edit page
Share this post:

Previous Post
[DDD Intro #25] Microservice and DDD: Is a Bounded Context a service?
Next Post
[DDD Intro #23] CQRS: When to separate read models and write models