CQRS – Designing domain events

by Allard BuijzeJanuary 27, 2010

logo Command-Query Responsibility Segregation (CQRS) is slowly but steadily gaining ground as an architecture that helps developers to develop scalable, extensible and maintainable applications. Events play a major role in this architecture, and the way you design these events greatly influence the extensibility of your application.

In this post, I describe some CQRS event basics and design considerations that help keep your application extensible.

The role of the domain event

In CQRS, domain events are the source of all application state changes. The diagram below shows a high-level overview of a CQRS architecture. The separation of the components that deal with commands and those that deal with queries is clearly visible.

cqrs_architecture-highlevel

When a command is executed, it will (most likely) change state of one or more aggregates (see DDD definition of aggregate here). As a result of these state changes, one or more events are dispatched.

These events are picked up by Event Handling Components that do whatever they were built to do. Common Event Handling components are those that update database tables in the query database, or those that send an email notification to a user. You can also use Event Handlers to execute commands on related aggregates.

The command handling component is not aware of the listeners that will act on the generated events. This is an important feature to make an application extensible. The downside is that you cannot foresee how events will be used and what information these event handlers will need.

Information contained in an event

Since events are broadcasted throughout the entire application, events should contain the information needed by the variety of possible event listeners that exist or might exist in the future. It is impossible to exactly predict what information is needed by event handling components. However, there are a few guidelines that will cover most of the cases.

Recently, someone asked me for advice on event design. He asked me if it was a good idea to use an “CustomerDetailsModifiedEvent”, which would contain the full customer details of the customer, after the change was applied. Although this sounds safe to do, it will get you in serious trouble at a later stage. I’ll go into that a little later.

Domain events should be as specific as possible. If only the address changed on a Customer, consider using an AddressChangedEvent. That event would then contain the new address. This allows components acting on these events to listen to more specific types of events. If you want to send a (paper) letter to someone if his address changed, the “CustomerDetailsModifiedEvent” won’t get you very far, as you don’t have a way to find out what exactly changed.

In some cases, it might be important to know the state as it was before the command was executed. For example, if it is vital for your application to know whether the person moved to another country (because the letter template is different in these cases, for example), then you should also provide the old address in the event. Since the event is generated by the component that also makes the actual change, this shouldn’t be too hard to implement.

<br>
public class AddressChangedEvent extends DomainEvent {</p>
<p>    private final Address newAddress;<br>
    private final Address oldAddress;</p>
<p>    public AddressChangedEvent(Address oldAddress, Address newAddress) {<br>
        this.oldAddress = oldAddress;<br>
        this.newAddress = newAddress;<br>
    }</p>
<p>    public Address getNewAddress() {<br>
        return newAddress;<br>
    }</p>
<p>    public Address getOldAddress() {<br>
        return oldAddress;<br>
    }<br>
}<br>

The code sample above shows the basic information contained in an AddressChangedEvent in case it is important to know the address of a customer before and after the modification. In this sample, the DomainEvent will contain information about the customer on which the address change was applied. Typically, this information is limited to the unique identifier of the aggregate.

Include the intent of the change

Let’s assume that our business case requires us to send a letter to each customer that changes address. This letter serves as some sort of validation that the address exists and gives the customer feedback that the address change was successfully handled.

In CQRS, we would just create a Event Handling Component that listens to events of type AddressChangedEvent and automatically send a letter to this customer. The information needed for the letter, such as customer name and gender are queried in the query database, since they are not contained in the event itself.

Consider an event with the following information:

AddressChangedEvent {

– aggregateId 123

– newAddress (“someStreat 2”, “13432”, “SomeCIty”) <- notice the typo in the street name

– oldAdress (…)

As a result of this event, a letter is sent. It is very unlikely that the typo in the street name will cause the letter not to arrive at the destination.

A little later, a sales representative notices the typo in the street name and want to change the address. This change will cause another letter to be sent, most likely to the same address. That’s not really the signal the business likes to send out.

In this scenario, we have captured two different intents of address changes: one is to report that a customer moved to another address, the other is to make a correction in an existing address. The way we deal with these different scenario’s is different.

Some components, like a database updaters, typically don’t care about intent. They will change the address to the new one, regardless of the reason. Furthermore, the information needed by listeners in both scenario’s is identical.

When designing events, consider making an abstract event type that defines “what” has changed. For example, an abstract AddressChangedEvent. We make it abstract, because we never want an instance of this type. This abstract then has a subclass for each intent of the change, representing the “why” of the state change. For example, AddressCorrectionEvent and CustomerMovedEvent. Since they both extend the abstract AddressChangedEvent, the both contain information about the old and new address.

Our event handling components now have a choice. If they don’t care about intent, such as the database updater, they will handle the abstract AddressChangedEvent. If they do, like our letter sender, they can listen to intent specific subclasses. In this case, the letter sender would only act on events of type CustomerMovedEvent.

Event immutability

Domain events represent a notification of a state change of an aggregate. It is a representation of something that happened. Whatever you do, what happened has happened. That said, it doesn’t make much sense to change any information in the event while it is being processed.

In CQRS, events are ubiquitous. This means that any component anywhere in the application could listen to any event. Once properties on these events start to change, it is very hard (if not impossible) to predict which component saw which information on the event.

Therefore, make sure all information contained in the event is immutable. At least from the time the event is dispatched. In java, and likely also in other programming languages, making fields immutable provide valuable multi-threading guarantees.

Event design in the Axon Framework

Recently, we released version 0.3 of the Axon Framework, an open source framework that helps you with the implementation of applications with a CQRS architecture. Axon Framework provides base implementations for most of the components that make up a CQRS architecture, such as some base classes for events.

All domain events should extend the abstract DomainEvent class. Axon will then ensure that the correct aggregate identifier is registered on the event and will also set a sequence number on the event that allows you to see in which order events were generated for an aggregate. However, the rules and guidelines mentioned above still apply to all domain events.

Starting at version 0.4 (planned for release mid February 2010), Axon Framework also provides support for Application Events and System Events. The former are events that do not represent a state change in an aggregate, but might be of importance to event handling components. The latter provide information about state changes of application components. They could, for example, report that a database is down or a mail server cannot be reached.

For more information about the Axon Framework, visit the project website: www.axonframework.org.