In the previous article, we talked about the concepts of CQRS and Event Sourcing and what problems they can solve. Although CQRS/ES can be implemented without any other framework or library, we recommend using existing tools. These tools simplify the development process while running developers focused on processing business logic and avoiding repetitive wheel building. In this section, we will choose the Axon framework to implement CQRS/ES.

What is the Axon?

Axon is a lightweight Java open source framework that helps you build scalable, extensible, and library-maintained Java applications based on the CQRS pattern. It also helps you prepare the environment for Event Sourcing. Axon provides implementations of all the important building blocks, such as aggregation, repository, command, and time bus. Axon can make developers’ lives easier.

Why Axon?

Axon lets us avoid configuring loads and manipulating data flows, and we can focus on customizing application business rules rather than creating boilerplate code. There are some advantages to using Axon:

  • Optimize event handling: It should be noted that events are published in a sequential order. The Axon framework guarantees the order in which events are executed.
  • Built-in test environment: Axon provides a test toolset that allows you to test your system at a specific time, which makes unit testing easier.
  • Spring Boot AutoConfiguration: Configuring Axon in a Spring Boot application is a very simple matter. Just provide the necessary dependencies, and Axon will automatically configure some basic components.
  • Support for annotations: Axon provides annotation support, which makes our code more legible and allows us to easily build aggregations and event handlers, regardless of axon-specific processing logic

Quickly configure Axon in the Spring Boot application

Axon provides integrated support for Spring Boot by default. Axon and Spring Boot can be integrated with a few simple configuration steps.

Step 1

The first step is to configure Axon dependencies in your project using the appropriate project build tool. Here is how to configure Axon using Gradle:

dependencies{
    compile("Org. Axonframework: axon - spring - the boot - starter: 3.2")
    compile("Org. Axonframework: axon - mongo: 3.2")
    testCompile("org.axonframework:axon-test:32.")}Copy the code

The first dependency gives us the most basic Axon components necessary for integration with Spring Boot, such as command centerline, event bus, and aggregation. The second dependency is to provide the base environment needed for our aggregation or event configuration library. The last dependency is used to build the previous test environment.

Step 2

Configure some of the Spring beans that Axon needs as needed. Such as EventHandlerConfiguration (components) is responsible for the control event handler behavior, if failed to perform one of the events, events will terminate all subsequent handling. This is optional, of course, but it is worth doing in your application to prevent data inconsistencies in your system. The configuration code is as follows:

@Configuration
public class AxonConfig {

private final EventHandlingConfiguration eventHandlingConfiguration;

@Autowired
public AxonConfig(EventHandlingConfiguration eventHandlingConfiguration) {
   this.eventHandlingConfiguration = eventHandlingConfiguration;
}

@PostConstruct
public void registerErrorHandling(a) {
   eventHandlingConfiguration.configureListenerInvocationErrorHandler(configuration -> (exception, event, listener) -> {
       String msg = String.format(
               "[EventHandling] Event handler failed when processing event with id %s. Aborting all further event handlers.",
               event.getIdentifier());
       log.error(msg, exception);
       throw exception;
   });
}}

Copy the code

The main idea here is to create an additional Configuration file (class annotated with **@Configuration). The class constructor injection of management by the Spring itself EventHandlingConfiguration dependencies. Due to binding, we can call on this object configureListenerInvocationErrorHandler (* *) and by recording an abnormal will spread to the upper to handle errors.

Step 3

We use MongoDB to Store all events that occur in the Event Store. To implement this function, you can do the following:

@Bean
public EventStorageEngine eventStore(MongoTemplate mongoTemplate) {
   return new MongoEventStorageEngine(
           new JacksonSerializer(), null, mongoTemplate, new DocumentPerEventStorageStrategy());
}
Copy the code

In this way, all events published on the event bus are automatically saved to the MongoDB database. With this simple configuration, we can use MongoDB’s data source in our application.

This is how simple Axon configuration is. Of course, there are many other configurations. However, we can use Axon’s features with the simple configuration described above.

CQRS is implemented using Axon

According to the figure above, creating commands, passing commands to the command bus and then creating events and putting events on the event bus is not CQRS. We must remember that changing the state of the write repository and reading the current state from the read database is the key to the CQRS pattern.

Configuring this process is also not complicated. When passing a command to the command gateway, Spring takes the name type as an argument to search for methods annotated with ** @commandHandler **.

@Value
class SubmitApplicationCommand {
   private String appId;
   private String category;
}

@AllArgsConstructor
public class ApplicationService {
   private final CommandGateway commandGateway;

   public CompletableFuture<Void> createForm(String appId) {
       return CompletableFuture.supplyAsync(() -> new SubmitExpertsFormCommand(appId, "Android")) .thenCompose(commandGateway::send); }}Copy the code

The command handler is responsible, among other things, for sending the created events to the event bus. It places the event object into the Apply () method statically imported by AggregateLifecycle. The event is then scheduled to find the expected handler, and since we configured the event repository, all events are automatically saved in the database.

@Value
class ApplicationSubmittedEvent {
   private String appId;
   private String category;
}

@Aggregate
@NoArgsConstructor
public class ApplicationAggregate {
   @AggregateIdentifier
   private String id;

   @CommandHandler
   public ApplicationAggregate(SubmitApplicationCommand command) {
      //some validation
       this.id = command.getAppId;
       apply(newApplicationSubmittedEvent(command.getAppId(), command.getCategory())); }}Copy the code

To change the state of the library, we need to provide a way to use the ** @eventhandler ** annotation. The application can contain more than one event handler. Each of them should perform a specific task, such as sending an email, recording or saving it in a database.

@RequiredArgsConstructor
@Order(1)
public class ProjectingEventHandler {
   private final IApplicationSubmittedProjection projection;

   @EventHandler
   public CompletableFuture<Void> onApplicationSubmitted(ExpertsFormSubmittedEvent event) {
       return projection.submitApplication(event.getApplicationId(), event.getCategory());
   }

Copy the code

If we want to determine the Order in which all event handlers are processed, we can comment out a class with ** @order ** and set a sequence number. The **submitApplication () ** method is responsible for making all the necessary changes and storing the new data into the write store.

These are key points to make our application adopt CQRS pattern principles. Of course, these principles can only apply to certain parts of our application, depending on business requirements. Event Sourcing is not appropriate for every application or module we are building. Caution should also be exercised when implementing this pattern, as more complex applications may be difficult to maintain.

conclusion

The implementation of CQRS and Event Sourcing is simplified using the Axon framework. For more details on advanced configurations, visit Axon’s website at docs.axonframework.org/.

ł ukaszKucik

The original address: www.nexocode.com/blog/posts/…

Translator: Tan Chaohong

Address: www.ramostear.com/articles/im…