The Command Query Responsibility Segregation (CQRS) and Event Sourcing (ES) patterns advocate the building of applications by treating it as a System of Events plus State rather than just as a System of State. Combined with DDD (Domain Driven Design), these patterns provide the foundational components to help build applications which are inherently Reactive, Scalable, Distributed and Event Driven. However implementing these patterns are complex. While the first tendency often is to build out these patterns utilizing custom built frameworks and tools, it quickly spirals out of control with a very high chance of failure as the various aspects of these patterns start to become clearer.
A wiser decision is to always utilize purpose-built platforms which help accelerate the adoption of these patterns quickly and effectively by providing a complete ecosystem of tools and techniques enabling developers to focus on what they do best - writing business code.
This blog post shall provide a holistic overview of 2 such platforms:
- Akka from Lightbend and
- Axon from AxonIQ
Before we get into the details, let us look at a high level overview of what I term as the CQRS/ES Capability Map i.e. these are the characteristics that any platform providing CQRS/ES capabilities should be implementing to qualify as “purpose built”.
The CQRS/ES Platform Capability Map denotes two main aspects -
- A logical infrastructure i.e. an API to support CQRS/ES operations preferably using DDD concepts.
- A physical infrastructure to support Event Persistence/Retrieval and Message Routing (for Commands/Queries and Events).
Let us tackle the logical infrastructure piece first i.e. the core API and how both platforms provide support for the patterns utilizing different techniques.
Logical Infrastructure - Akka
Akka is based upon the Actor computing model which provides an abstraction to build fault-tolerant, concurrent and distributed systems. It is based on the notion of messages being passed between individual actors that receive these messages in their own “mailbox”, interpret these messages and accordingly react to them, all in an asynchronous manner.
One of the components within the Akka platform is the “Akka Persistence module” which provides the “Akka Persistence Typed API” (Scala/Java) to help implement ES/CQRS operations. These operations are typically implemented on “Aggregates” as they represent the state of any Bounded Context within the DDD parlance. The API does not provide any DDD specific marker capabilities to denote an Aggregate, so essentially you can take any Entity and create an Event Sourced Actor out of it. An Event Sourced Actor will have State (pretty obvious) / Commands / Command Handlers / Events / Event Handlers and Replies. And since in Akka Typed everything is a behavior, this is all glued together using the “EventSourcedBehavior” which can be marked on an Actor.
A typical “Command Message” flow is as follows. Any Event Sourced Akka Actor recieves a message indicating a command needs to be processed. The Actor delegates to a Command Handler, which loads the state by replaying the past events that have already occured (or a new state if it is the first Command). Command Handlers validate the Command message and depending upon the execution result will return Effects. These Effects could be the persistence of an Event(s) (denoting the successful execution of a command) within an Event Journal, could be to do nothing, stop the Actor or raise an Unsupported Command Exception. Once the persistence is successful it will delegate the processing to the Event Handlers within the Aggregate to mutate state. The general requirement for Event Handlers defined within Aggregates must be pure functions as they will be used when instantiating the Aggregate and replaying the event journal. The API provides support for this.
The Akka documentation provides a style guide on how you can go about building your Event Sourced Actor including recommendations on how to place each of the operations mentioned above within your Actor State classes.
While this takes care of the Command side of things, we also need to handle the Query Model. For this Akka provides “Akka Projections” a significant upgrade over the previous “Akka Persistence Query” module. Akka Projections help you build read/query models by pointing to any Akka Stream Source and not just Event Journals. In addition to that it also now tracks offsets enabling replays and higher resiliency.
The core API also provides support for Snapshots/Schema Evolution/Testing and State Machines.
Logical Infrastructure - Axon
To help implement the logical concerns, Axon provides the Axon Framework. Axon Framework provides a complete API to help build CQRS/ES based applications based on DDD. Axon Framework treats all Commands / Queries and Events by designating them as “Messages”. This enables the usage of the Location Transparency architectural characteristic which advocate that Application components need to interact with each other in a uniform fashion defined by explicit message passing. Axon Framework takes this a step further by not even letting the sender of a “Message” know "what" the recipient is, so that you're even more free to decompose components in an evolutionary way. The Framework provides first class integration with Spring Boot but if you would like to build the plumbing yourself it offers a very handy configuration API based on Java.
The Command Message flow follows the same mechanism as above but Axon does do things differently. Since it provides an implementation of the Location Transparency pattern the framework has the concept of a “Bus”. Command Messages are then placed on a “Command Bus” which then routes it to the appropriate Command Handlers. This capability helps in developing components which are not aware of other components resulting in the ability to scale out or distribute components as needed which is a strong selling point of the framework. To build your Event Sourced Aggregates Axon Framework provides a set of annotations which is based on a combination of DDD/CQRS concepts. This includes annotations to mark Aggregates/Command Handlers/Event Sourcing Handlers and Event Handlers. Command Handlers raise events which are handled by the Aggregate itself to mutate state and then persisted into the Event Store (the Journal). The API provides support for Event Handlers within an Aggregate to just mutate state.
To help build Projections/Read Models, Axon Framework provides a very powerful mechanism called “Event Processors”. Event Processors are tagged to Event Handlers and help in building highly scalable and resilient Query Models including support for offset tracking, exception handling and segmentation. Similar to “Command Bus”, Axon Framework also provides support for “Event Bus” and “Query Bus”. The Event Bus is responsible for routing Events to “Event Handlers” while the Query Bus is responsible for responding to Query Messages and routing them to “Query Handlers”.
The framework also provides support for Snapshots/Upcasters for Schema Evolution/Testing and Sagas.
Physical Infrastructure - Akka
In terms of the physical infrastructure, the Event/Snapshot Journal could either be Cassandra, a JDBC source or Slick DB. Your Projections could point to any of these (or even Kafka) since all it needs is an Akka Stream Source. In terms of distributing your Event Sourced Actors, Akka does offer the capability of Clusters and a new module called Replicated Event Sourcing (which is still WIP as of this writing)
Physical Infrastructure - Axon
Axon provides the Axon Server which is a purpose built Event Store optimized for Event Sourcing operations. In addition to being an Event Store it also provides a physical implementation of the Command Bus / Query Bus and Event Bus thus effectively acting as a Message Router also. Axon Server is available in two editions - Axon Server SE (Standard Edition) which is suitable for single node deployments and Axon Server EE (Enterprise Edition) which supports more advanced capabilities (e.g. Clustering) in case you have more stringent Availability and Resiliency requirements. While the recommended Message Router/Event Store is Axon Server, Axon also supports a set of open source components that can be used to build out your CQRS/ES infrastructure e.g. using Spring Cloud for creating a Distributed Command Bus as well as any Relational Database such as Postgres as an Event Store.
To conclude, both Axon and Akka provide an enterprise grade ready to use platform to help build applications utilizing the CQRS/ES patterns. Possibly a good integration point between the two could be the choice of Axon Server as an Event Journal for Akka? A good idea for a future blog post.