Home Ecommerce DDD - Hands-on Domain-driven Design and Event Sourcing - 3/6
Post
Cancel

Ecommerce DDD - Hands-on Domain-driven Design and Event Sourcing - 3/6


In the previous post, previous post a bit more into bounded context and some of the building blocks of the implementation. Now, let’s extend the implementation to domain events.


Domain Events


Before going to event sourcing, let’s be clear that there are many definitions for events in software design; not all events are necessarily domain events, and domain events do not always imply event sourcing.

Domain events describe immutable facts that happened as the result of aggregate behavior. Ideally, aggregates models should have behaviors as opposed to anemic domain models and consequently aggregates act as factories for the domain events. The ubiquitous language again plays a huge role here.

When naming events, consider their context with high-value semantics, and use a combination of the [noun + past tense verb ]. This dependency with the bounded context means domain events are only considered valid within their context boundaries.

Integration events?


Although they look very alike, domain and integration events reach different ranges. While Domain events can be used as notification messages to trigger side effects across aggregates in the same bounded context using a synchronous in-memory transaction, Integration events can trigger side effects across different bounded contexts or other systems, using asynchronous operations. Async operation for integration events is needed as they fan out of the context boundaries, and the response time is not deterministic.

Overall, for steaming events, you can use a Message Broker/Event Bus, and there are many ways to achieve it. I’m using Kafka in this project for learning purposes, but I also had good experiences using RabbitMQ.


What is Event Sourcing?


In a few words, Event Sourcing is an architectural pattern. The art of logging events that surround us is not something new in our lives, but for software design purposes, it’s known that Greg Young shaped the technique into the form we call Event Sourcing nowadays.

Assuming we have our domain events all in place, using a special storage called event store we can persist these domain events in chronological order and make them our source of truth. For this to work consistently, the store has to be immutable, which means events are always appended but never changed or deleted. Furthermore, Event Sourcing allows us to shift from the conventional approach where retrieved data goes only for the last state of the domain object into reading the sequential stack of events, rehydrating the object until it gets into the latest state.

This architectural pattern has some nice built-in perks, like the fact we no longer need to worry about impedance mismatch, once we store the event integrally and serialized. Also, the event timeline generates an intuitive log for granted, which can be very useful for many tracking purposes as it shows the whole pathway to the latest state. Also, writing and reading events are completely separate operations that can scale up and perform independently, and that is why event sourcing needs CQRS.

Once again, no rule of thumb for the technology you use to implement Event Sourcing. Some good players in the market, such as Event Store do the job very efficiently, but I’ll be fully using Postgres as document database, reason why usiong MartenDB was the natural choice.

Beware, the learning curve can be pretty deep with all the details, depending on your implementation. Consider things like handling concurrency, where multiple users can edit the same record simultaneously, and you need to ensure they happen in the proper order. There’s a very nice article where the author covers this subject excellently, and I don’t dare try to explain it better. He also maintains this awesome repo that inspired me with many ideas to convert this study project into something event-sourced.

CQRS

CQRS architectural pattern (Command Query Responsibility Segregation) comes to the rescue to make sense of it and that’s why people often mention how CQRS and Event Sourcing go very well together.

Commands are defined in the domain and express user intents and actions. Commands will be the triggers to change the state of our aggregate to fan-out events on the write side of the coin. Conversely, we can perform queries to read data from a materialized view of the same events. I will discuss projections in the next post; don’t worry.


Hands-on


Now, let’s take the Customer aggregate root for a simple example. After the domain invariants are validated, the domain object is built, the AppendEvent and the Apply methods are called sequentially:

AppendEvent(@event);
Apply(@event);

AppendEvent is defined in the AggregateRoot base class, and it adds the event to the uncommitted events Queue of IDomainEvent. Then afterward, the Apply method mutates the aggregate state based on the @event argument it is overriding. Each applied event mutates a corresponding part of the aggregate.

For example, UpdateInformation command does not update customer fields right away but creates and stacks a CustomerUpdated event with a payload to be applied right after:


Final thoughts


All we saw so far works with in-memory collections, which technically can’t be considered Event Sourcing yet. In the next chapter, I’ll show you how to persist these events into the write database and project them into the read database for querying, technique as known as Projections. See you soon!


Check the project on GitHub



This post is licensed under CC BY 4.0 by the author.