If you’re a developer in an ever-lasting quest of finding the best route for building basic building blocks for applications, then Axon just might be your secret weapon and sidekick. Axon is an open-source framework for event-driven microservices and domain-driven design (DDD). It helps developers build easily scalable and maintainable applications by providing implementations of some basic building blocks. In this article, we aimed to give you just the right amount of information to get you started on Axon and how it helps you implement applications with CQRS (Command Query Responsibility Segregation) and Event Sourcing in mind.
Check it out and thank us later!
Before we get into Axon, we need to understand some basics about CQRS and Event Sourcing.
So, CQRS stands for Command Query Responsibility Segregation and is an architectural pattern. The change it brings is the division of the conceptual model into two separate models:
- Command Model – intended for updating
- Query Model – intended for displaying the information
The reason for this approach is that in many problems, especially in complex domains, the same conceptual model for commands and queries leads to an overly complex model that doesn’t perform either of them well. So in CQRS, we separate the part that changes the state of the application from the part that gets the information about the current state of the application.
By combining CQRS and DDD (Domain Driven Design), the application is split into these two components, and each of these components focuses on its own job – update or query. While the commands are executed, the query components are updated asynchronously with events. This approach makes the CQRS architecture scalable, expandable, and easier to maintain.
About Event Sourcing
The current state of the application can be determined through queries, yet sometimes there is a need to know not only where we are, but how we got there.
Event sourcing (ES) helps us do this by storing all changes to the state of the application as a sequence of events. This way we can’t only find out the current state, but also recover all previous states of the application.
The importance of CQRS and Event Sourcing
When we talk about the state of the application, we also mean the state of the business entities inside the application. Whenever the state of the entity changes (a command occurs), a new event is appended to the list of events. Saving the event is a single, atomic operation. The state of the entity and the entire application can be restored by replaying the events that occurred.
Thus, in event sourcing, the events aren’t the only byproduct of the command. They’re the source of the application’s state. The current state isn’t explicitly stored in the database, but it is implicitly stored as a series of events. The events are stored in the event store, which is basically a database of events and is the only source of truth. The complexity of software applications is rapidly increasing, and projects and codebases are getting bigger with functionalities being added or modified. The addition of what seems to be a seemingly simple function can result in the entire application having to be disassembled. That being said, scalability is a must with the number of users we have today.
We have to decide whether to build our application as a monolith or as a microservice.
- In monolithic applications, each component is interconnected and works as a single service.
- In the microservice approach, the application consists of many small, loosely coupled, and independent services.
The microservice approach can be very difficult to implement from the outset. Where the boundary is between each microservice and how the domain can be “split” into smaller domains is often difficult to see. Therefore, it’s much easier to start with a “structured monolith” and let the individual microservices emerge over time – a so-called evolutionary approach. CQRS and Axon Framework make it easy for us to use this approach.
Another example where CQRS can be useful is shown in the figure below.
Normally, any operation in an application is done in such a way that you write a code that executes a certain logic and writes the data to the database. Once the operation is complete, the result of the data update is returned. And this is the basis and usual way of interacting with the information system: We create new records and read, update and delete the existing ones. The conceptual model is usually created for manipulating the core elements of the model and is usually the conceptual representation of the domain. The persistent store is also created to be as close as possible to the conceptual model. These different levels of representation can become quite complex over time. In CQRS, the same data is represented by different models.
Implementation of CQRS and Event with Axon Framework
Now that you’re familiar with the principles of CQRS and Event Sourcing, the next logical question would be, okay, how can I implement this? There are several ways, and we’ll go into more detail about how to do that with the Axon Framework.
As we’ve hinted, Axon Framework is an open-source Java framework that helps us develop applications based on three core concepts – CQRS, Event Sourcing, and DDD.
In event-driven microservice systems, it’s very important that the communication between services is efficient, reliable, and easy to monitor. Message routing shouldn’t require manual configuration, and adding new services should be relatively easy. In the Axon Framework, all internal communication – the exchange and routing of messages and the implementation of these processes – is done under the hood.
Axon Server, the companion product to Axon Framework, acts as a message router and is designed to store events for event sourcing. It’s scalable and doesn’t require difficult tuning.
All communication between components is done using the message objects. This makes the components easily distributable and scalable. All messages should be immutable, which means that the addition of new data in a message actually means creating a new message based on a previous one, with new information added to it. Because of this, messages are safe to use in a multithreaded and distributed environment.
Even though all messages implement the Message interface, there is a clear difference between different types of messages and the ways they are used/treated. All of them contain a payload, metadata, and a unique identifier.
There are three types of messages in the Axon framework:
Commands are implemented as read-only POJOs and they express the intent to change the application’s state. They always have exactly one place where they are handled. Even though the sender does not care which components handle the command, it may be interesting to know the outcome of it. That’s why command messages sent over the command bus allow for a result to be returned.
Events are objects that represent something that has happened in the application and they are created by changes in the state which are initiated by commands and their handlers. The (typical) source of an event is the Aggregate – when something happens inside the Aggregate, the event is raised.
In Axon, it is recommended that each event is serializable.
The type of message used depends on the origin of the event. In addition to the usual message attributes, such as id, EventMessage also contains a timestamp (i.e., the exact time when the event occurred). It also contains the type and id of the aggregate in which the event was created, as well as the sequence number of the event within the aggregate, which is very important to us because it allows events to be repeated (i.e., replaying all events in the order in which they actually occurred). On this basis, we can recover the state of the application at a given moment.
Queries represent a request for information or state. In Axon, they can be handled in multiple places, and the client can indicate whether he wants a result from one or from all available query handlers.
DDD and CQRS give us a set of useful building blocks. Here are some that are important in the context of applications that use Axon.
Aggregate represents the current state of the entity and is always kept in a consistent state. As such, aggregate is the main building block of the command model in any CQRS-based application.
The term Aggregate is defined by Eric Evans as a “cluster of associated objects that we treat as a unit for the purpose of data changes”.
They are explicitly present in the Command Model (because that is where the change is initiated) and should not be directly queried. Aggregates handle the commands and based on them, they dispatch appropriate events. The state of the Aggregate is the sum of all events that the Aggregate saw. The current state of the Aggregate can be loaded using the Event sourcing handlers and by replaying the events.
Not every command can be executed in a single atomic transaction. While ACID transactions are not always necessary, we still need some form of transaction management. That’s where sagas come into play. Sagas are first class citizens in Axon. They are described as a long running business process that coordinates the interactions between components. Sagas are commonly used as a coordination mechanism between different aggregates in order to eventually achieve consistency.
The aggregate listens for commands and emits events. A saga listens to events and emits commands.
Sagas should be kept as simple as possible. They separate logic from process – the aggregates contain business logic, and sagas ensure that aggregates collaborate in a well-defined business process.
View Models or Projections
In CQRS, View Models (also known as Projections or Query Models) are used to efficiently expose information about the application’s state. Unlike Command Models, view models focus on data, rather than behavior. They are modeled to adapt to the intended audience of the model.
One of the main benefits of Axon Framework and CQRS is the separation of command and query models (the load for reads and writes is separated). The separation of components makes horizontal scaling easily achievable.
Applications evolve through time; they grow more complex. Until a live production environment is reached, a lot of modifications will be made, due to new information being acquired or requirements being changed. With Axon Framework, changes that are made in the scope of a component will not have any direct impact on the behavior of other components because they are loosely coupled. Aside from that, new components are easily added – simply connecting the new component to the message bus, and no changes need to be made to the already existing components.
Most applications require communication and exchange of information with multiple external services. This entails writing code in different places to trigger communications with third-party systems. In Axon, you can easily add new components that provide integration with such systems, without intruding on the original application behavior.
In some cases, there is not a clear distinction between the query and command model, so their separation is not that easy. In domains where it doesn’t really make sense to do that and in applications that don’t conform to this model of application development, additional and unwanted complexity is brought. This can have a negative effect on the productivity and efficiency of the team and overall development. So before implementing the system based on CQRS and ES, you should really think if that is suitable for your application.
Insufficient attention to event modeling
Events should accurately represent something that has happened in the system, and it is important to give extra care when modeling them. Event Sourcing can be very beneficial, we can have a perfect record of events that can be used in business for auditing, analytics, and many other purposes. But this doesn’t work if events don’t correctly represent things that have happened in the domain.
Ignoring event serialization
Events in ES are stored in the event store and represent the single source of truth. To achieve this, they need to be translated into something that can be stored, which is called serialization. We must remember that this is something that we commit to long-term when building applications that rely on ES.
Paying insufficient attention to aggregate boundaries
Incorrect design of the aggregate may have big consequences which are hard to correct later on. If the aggregates are too small, it is hard to maintain their instances in a consistent state. Such applications usually have many Sagas. If Aggregates are too large, the performance will suffer – loading of the Aggregate will become a heavy operation, and handling the commands that target the same aggregate instance becomes challenging.
Now that you have learned the basics of the Axon framework, the need to implement it, and why it’s a powerful foundation for building an application that takes advantage of CQRS and Event Sourcing, you are ready for the next step – implementation. Follow Hybrid IT Solutions as we will cover more useful topics in our upcoming blogs. Keep learning!