Trifork Blog

Axon Framework, DDD, Microservices

CQRS – Designing domain events

January 27th, 2010 by
|

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.

public class AddressChangedEvent extends DomainEvent {

    private final Address newAddress;
    private final Address oldAddress;

    public AddressChangedEvent(Address oldAddress, Address newAddress) {
        this.oldAddress = oldAddress;
        this.newAddress = newAddress;
    }

    public Address getNewAddress() {
        return newAddress;
    }

    public Address getOldAddress() {
        return oldAddress;
    }
}

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.

6 Responses

  1. January 28, 2010 at 03:49 by banq

    In My framework, Domain events is a async message between domain model and component, it should be in domain layer, no matter presentation layer.

    see my domain events impl.: http://www.slideshare.net/banq/ddd-framework-for-java-jdonframework-2881760

  2. February 20, 2010 at 21:37 by Joris Kuipers

    Just read your blog entry, and was thinking about your advice to use subclasses of abstract event types to capture intent. Wouldn’t it be better and easier to capture intent through another means than inheritance, like aggregation? You’re now getting yourself into problems if your events want to express multiple intents, for example. You could just fire multiple events in that case, but that’s likely to lead to unwanted side effects like duplicate updates of your database. The event itself will also likely not differ in state nor behavior for different intents, as far as I can see. Intent sounds like something that shouldn’t be captured by a type to me.

    Frameworks like Spring Integration and Apache Camel, which implement the Enterprise Integration Patterns to allow you to build an event-driven application, have the notion of a message that contains both a payload and headers. I could even see intent (or intents) ending up as a header instead of as part of the payload in those cases. It saves you from having to define multiple concrete types and allows you to reuse the same event types while expressing different intents quite easily by separating these concerns. Pre-defined headers make it very easy then to do things like filtering or routing based on expressed intent.

    What do you think?

  3. February 21, 2010 at 19:54 by Allard Buijze

    Hi Joris,

    in CQRS, a Domain Event is a result of a command. That means that there is always a single one [a single intent, red]. I don’t think the situation where you need more than one intent for an event is one you’ll find in CQRS.

    You state that the behavior won’t change for different intents. The only behavior an event will have is the exposure of its state; they are immutable. But it is possible that different intents contain different information. The information in the abstract base class, of course, is the same for each intent. Take the example of the CustomerMoved Event. It might be possible to include the old address. In an AddressCorrectedEvent, that might not really be that useful.

    Domain Events and the Messages used by Spring Integration have a lot of similarities. I do, however, see one major difference between the two. Domain Events are purely means as a “publish and forget” message to notify components that something happened, whereas messages have a more general purpose. You could say that events are a type of message. The CQRS command is another example of those.

    Your comparison of the way CQRS deals with events and the role of messages in Spring Integration does not come unexpectedly. In the Axon Framework, the Spring Integration Event Bus, which dispatches events on a Spring Integration channel, was one of the first building blocks implementations. I believe the two complement each other nicely.

    That said, I do have to admit you have a valid point. The intent might very well be captured as a property in the event itself. Personally, I try to be as explicit as possible, a concept that stuck with me since my training with Eric Evans. In CQRS, the Domain Events are an important part of the model. That’s why I personally like to include the different possible intents are part of that model. That might mean that I have a few more classes in my arsenal of event implementations. But whatever you choose, I am sure that “routing-by-type” is just as easy as “routing-by-header-value” in Spring Integration. In the Axon framework, however, listening to events based on their type is a lot less verbose than acting upon events based on a property value.

  4. March 31, 2010 at 11:33 by Thomas

    Great article !

  5. November 1, 2010 at 08:54 by Tarek Nabil

    I noticed that your Address domain object does not contain any sort of id; I guess that’s because it’s a VALUE OBJECT and you do not want to expose its id outside the aggregate.

    But for a component that will use that event to update the view data store, how does it locate the old address to update it if the customer has more than one address? Will it have to query for an address where all the inidividual fields match those in the event?

  6. November 1, 2010 at 09:02 by Allard Buijze

    Hi Tarek,

    interesting question. I guess the answer should be “yes”, because the identity of a Value Object is represented by the combination of its attributes. Therefore, there is no separate “id” field. So to check for equality of an address, in this case, you’d have to compare all its fields.

    Alternatively, you could store a hashcode as a quick lookup for an address. That could make the lookup a little easier/faster. You just have to make sure that different addresses generate different hashcodes.

    Cheers,

    Allard