Does CQRS need a service bus

Event sourcing frameworks in comparison: Axon, Eventuate & Spring Cloud Stream

Of: Thomas Bayer, Oliver Weiler
Date: 4th April 2018

Event sourcing enables the loose coupling of microservices. Event sourcing can be implemented on the basis of a message broker such as Apache Kafka. An interesting alternative is to use a special framework for event sourcing. These frameworks can facilitate implementation, make the middleware interchangeable or offer additional features such as snapshotting.

This article compares event sourcing frameworks for the Java platform and serves as a decision aid for the selection.

1 Problems of Distributed Microservices

Microservices make it possible to develop and deploy services separately. In addition to various advantages, microservices also have a number of disadvantages. The distributed state represents the greatest challenge.

1.1 Distribute data

Each individual microservice takes care of its own data storage and can use the database technology that is optimal for it. Depending on the requirements, both SQL and NoSQL technologies can be used.

This makes the exchange of data between services difficult, since, for example, data must be transferred from a relational model to a non-relational one.

1.1 Transactional Security

When services exchange data with one another, this can be done using synchronous protocols such as HTTP. These protocols have several disadvantages:

  • The creation of a data record is associated with a certain delay (latency), as the change first has to be distributed to all parties involved.
  • If a change goes wrong, for example because a service is not available, the database is now inconsistent across all services.

Distributed transactions seem to offer a solution. Distributed transactions are complex, may only run for a short time and the outcome depends on a heuristic in the event of an error.

If you want to deal with the topic in more detail, the article: Distributed Transactions: The Icebergs of Microservices by Graham Lea has been recommended.

2 terminology

This section describes important terms for understanding event sourcing and comparing the frameworks.

2.1 Event sourcing

With event sourcing, services generate events as soon as a status change occurs. Other services can subscribe to these events and in turn change their status and create further events. In order to avoid inconsistencies, every change in status is communicated via a bus in event sourcing. After a short period of time, all services will be in a consistent state. One then speaks of Eventual consistency. By reading in all events again, the status of each service can be reconstructed or new services can be synchronized.

2.2 Snapshotting

The application status can be reconstructed by reading in the entire event history. Since this process can take a long time, a so-called snapshot is often created periodically or manually.

An event containing the aggregated application status is placed on the bus. All previous events are discarded and only the events after the last snapshot have to be aggregated.

Snapshotting is purely a performance optimization and should only be used if there is actually a performance problem.

2.3 Eventual Consistency

At Eventual consistency the consistency guarantees are loosened in order to achieve high availability. Changes are transferred asynchronously to the services involved, i.e. the changes are only visible in these services after an indefinite period of time.

The great advantage of this consistency model is that services do not have to be available at the time of synchronization, and the service that is responsible for the status change does not have to wait until all services have confirmed the change.

One disadvantage, of course, is that a client may see outdated data. Surprisingly, as experience has shown, this limitation poses little or no problem for many practical applications.

2.4 Command Query Responsibility Separation

Until Spring Cloud Stream all frameworks presented here are based on the CQRS pattern.

CQRS is an alternative to the classic layer model. CQRS separates the write operations (commands) from the read accesses (queries). Queries return results and have no side effects. Commands update the domain objects (aggregates) of a service, but do not return any results.

This separation allows the data to be stored in an optimized form for read access (read model).

The actual change of state takes place via events. CQRS is often used with event sourcing, but does not necessarily require it.

3 Spring Cloud Stream

Spring Cloud Stream von Pivotal is the newcomer to the event sourcing framework. The framework is not tied to a specific broker. Due to the abstraction of the broker, RabbitMQ can also be used in addition to Apache Kafka.

3.1 How it works

Communication with the broker takes place via so-called channels, a distinction being made between reading and writing channels. Channels are created using the annotation @EnableBindings connected to the broker and have no knowledge of the middleware used. This makes it possible to exchange the broker at runtime or to address different brokers via a common interface.

@EnableBinding ({Source.class, Sink.class}) @SpringBootApplication public class Application {

To write events you can Output channels as Spring Bean injected become. Methods can be used to process events with @StreamListener be annotated. About the target Attribute is configured on which input channel the method listens on.

@StreamListener (INPUT) public void on (ArticleCreated postCreated) {articleRepo.put (postCreated.getId (), new Article (postCreated.getId (), postCreated.getName (), postCreated.getPrice ())); }

The StreamListener is able to handle the Content-Type Interpret the header of a message and automatically convert the payload into the desired format (mostly Java POJOs). The Content-Type Header can be set explicitly when writing or configured via properties.

output.send (withPayload (articleCreated) .setHeader (CONTENT_TYPE, "application / json") .build ());

3.2 Main features

Among other functions, these are the main features of Spring Cloud Stream:

  • Middleware independence through abstraction
  • Publish-subscribe support regardless of the middleware used
  • Consumer group and partitioning support regardless of the middleware used
  • Automatic Content-Type Handling
  • Support for schema extensions
  • Based on Spring Integration
  • Good test support

3.3 Advantages

  • Middleware independence
  • Suitable for implementing enterprise integration patterns thanks to Spring integration
  • Seamless integration with Spring Cloud

3.4 Disadvantages

  • No snapshotting
  • Abstractions such as aggregates or commands have to be implemented yourself
  • Early development stage, features such as error handling are available but hardly documented

3.5 configuration

Thanks to Spring Boot's autoconfiguration, only a minimum of manual configuration is required. Both framework and middleware-specific properties can be defined using properties. Input and output channels are declaratively defined using interfaces.

The annotation @EnableBinding ensures that Spring Cloud Stream For each interface and the channels defined there, an implementation is created and made available as a bean.

3.6 Serialization Format

The serialization format is set either via properties (global or per output channel) or via the contentType Header set, where the contentType Header takes precedence over the value in the property.

If the format is neither via Properties nor via the contentType is set, JSON is assumed as the content type.

In the case of middleware that does not natively support headers, the framework adds missing meta information to outgoing messages.

Text, JSON, serialized Java objects and Avro are supported as formats. The actual payload is always transmitted as a byte array.

If the predefined formats are not sufficient, you can define your own types and converters.

Methods that use @StreamListener Register annotation on an input channel, automatically take care of the deserialization without the Content-Type must be specified.

3.7 CQRS

CQRS is with Spring Cloud Stream possible; in contrast to the competition, the framework does not offer any support for the implementation of the design pattern.

This simplifies entry and makes the framework attractive for scenarios in which you want to use event sourcing without CQRS.

3.8 Documentation

The documentation is detailed and introduces the developer to the framework using simple code examples. Concepts, usage and configuration are explained step by step. A GitHub repository offers numerous examples for topics such as testing or the support of Kafka streams.

4 Axon Framework

Axon from the company AxionIQ is a "CQRS framework for scalable, high-performance Java applications" and with 7 years of development it is the oldest of the frameworks presented here. It implements an architecture based on the CQRS pattern and provides ready-made abstractions for its implementation.

4.1 Functionality

As is usual with the classic CQRS, Axon also divides the application into a reading and a writing component.

About a Command bus commands are sent to a handler.

return commandGateway.send (createArticle) .thenApply (id ->…);

This loads the associated aggregate (a combination of business objects) from the so-called repository and validates the command. After successful validation, the handler generates an event and sends it to the event bus. The aggregate defines one Event handlerwho receives the event and carries out the actual change of state.

@Aggregate public class ArticleAggregate {@AggregateIdentifier private String id; private string name; private BigDecimal price; @CommandHandler public ArticleAggregate (CreateArticle createArticle) {apply (new ArticleCreated (createArticle.getId (), createArticle.getName (), createArticle.getPrice ())); } @EventSourcingHandler public void on (ArticleCreated articleCreated) {id = articleCreated.getId (); name = articleCreated.getName (); price = articleCreated.getPrice (); }

Further EventListener can be registered on the event bus. These update the one optimized for read access Read model or notify external services.

@EventHandler public void on (ArticleCreated articleCreated) {Article article = new Article (articleCreated.getId (), articleCreated.getName (), articleCreated.getPrice ()); (article);

For the administration of the Read models is often Spring Data used, since Axon 3.1 also own abstractions for query handling have been provided, e.g. Query Gateway and Query Bus.

4.2 Configuration

Similar to Spring Cloud Stream Spring Boot’s autoconfiguration ensures that reasonable standard settings based on the current class path are accepted.

Components can be customized by implementing bean methods. The configuration via properties is partly possible, but not to the same extent as with Spring Cloud Stream.

4.3 Serialization Formats

The serialization formats supported include XML, JSON and serialized Java objects. Own formats can be implemented via a serializer interface.

4.4 CQRS

Axon implements all common abstractions in the CQRS environment such as aggregates, commands, events and queries. This simplifies the implementation of a CQRS based architecture.

The framework is not suitable for the use of event sourcing without CQRS. The use of a leaner alternative such as, for example, is recommended here Spring Cloud Stream.

4.5 Documentation

The documentation describes in detail the architecture of the framework, the individual components and their configuration. The description of the application scenarios in which the use of the framework makes sense is positive. A continuous tutorial exists for version 2 of the framework.

Furthermore, Axon provides several completed sample projects that document the use of the framework in practice.

4.6 Main Features

  • Implementation of the most common CQRS, DDD and event sourcing abstractions (commands, queries, aggregates, events)
  • Spring Boot support from one Spring Boot Starter
  • Distributed business transactions through Sagas
  • Snapshotting

4.7 Benefits

  • Existing abstractions simplify the correct implementation of the CQRS pattern
  • Spring Boot Starter allows you to quickly set up an executable project
  • Due to its long development time, the Axon Framework has a certain degree of maturity

4.8 Disadvantages

  • Currently no support for Apache Kafka or RabbitMQ
  • The CQRS pattern represents a major entry barrier for laypeople

5 Eventuate Framework

Similar to the Axon Framework, Eventuate's architecture is based on the CQRS pattern and an event-driven programming model.

A special feature of Eventuate is the self-implemented event store. Events are not placed directly on the event bus here, but are first written to a database. The changes are read from the database's transaction log via CDC (Change Data Capture) and then written to the event bus. The advantage of this architecture is that existing applications can be converted more easily into an event-driven architecture.

5.1 Functionality

Services receive external calls, map them to commands and send them to new or existing aggregates. The received commands are validated by the aggregate and generate one or more events, which are stored in the event store.

EventListener update aggregates or data models optimized for read access via received events.

5.2 Configuration

The configuration is typically done via Spring. Since currently no Spring Boot Starter for Eventuate exists, all components have to be created manually as Spring Beans.

The configuration effort is slightly higher than with the competitor products presented.

5.3 Serialization Formats

The only serialization format currently supported is JSON.

5.4 Documentation

The documentation describes in detail the motivation for using event sourcing. The concepts and architecture of the framework are discussed. There are code examples for the individual CQRS components; there is no continuous tutorial.

5.5 Main Features

  • Open Source (Eventuate ™ Local) and SaaS (Eventuate ™ SaaS) versions
  • Web console for real-time display of aggregates and events
  • Implementation of the most common CQRS, DDD and event sourcing abstractions (commands, queries, aggregates, events)

5.6 Benefits

  • SaaS version simplifies deployment
  • Integration with the Spring Framework
  • Numerous examples

5.7 Disadvantages

  • Own event store implementation
  • No spring boot starter
  • No test support
  • No continuous tutorial

6 Overview

Spring Cloud StreamAxonEventuate
LicenseApache 2.0Apache 2.0Apache 2.0 (Open Source Version)
GitHub Stars260963314
First release201620112016 (Open Source Version)
CQRS support-xx
HostingCloudFoundry, on-premiseOn-premise, cloud hosting possible but not explicitly supportedAmazon AWS, on-premise
Test supportxx-
Serialization formatsText, JSON, Java objects, AvroXML, JSON, Java objectsJSON
Supported messaging middlewareApache Kafka, RabbitMQJDBC, JPA, MongoDBKafka + MySQL (open source version), proprietary event store (commercial version)

7 Conclusion

Event sourcing poses many problems for the developer, which event sourcing frameworks try to solve in different ways.

Provides for easy event sourcing Spring Cloud Stream is currently the method of choice. With little configuration, the connection to the broker is established and events can be generated or consumed. Thanks to abstraction, you are not tied to a special middleware, which prevents a possible vendor lock-in. The Apache Avro Integration makes Spring Cloud Stream very attractive when exchanging events with other non-JVM-based services, as the data schema does not have to be duplicated.

If you want to use event sourcing in connection with CQRS, the Axon Framework is the ideal solution. It provides the appropriate abstractions for CQRS and makes the implementation of the pattern much easier.Pure event sourcing is possible with Axon, but the architecture based on CQRS is rather a hindrance.

If time-to-market is an important factor, Eventuate could be an interesting alternative to Axon, since Eventuate already has the necessary infrastructure as a Software as a service provides. The developer can then concentrate fully on the development without first having to set up the required infrastructure.