As systems grow in complexity, traditional CRUD-based architectures struggle with scalability, decoupling, and consistency. Event-driven microservices, combined with CQRS (Command Query Responsibility Segregation) and event sourcing, offer a more resilient and scalable approach.

In this post, we’ll explore how to build event-driven microservices in Java using Axon Framework, a powerful toolkit that simplifies CQRS and event sourcing while integrating well with Spring Boot.


What Is Axon Framework?

Axon Framework is a Java-based toolkit that helps implement:

  • Command-Query Responsibility Segregation (CQRS)
  • Event sourcing
  • Domain-Driven Design (DDD)
  • Event-driven architecture

It provides abstractions for:

  • Command buses
  • Event buses
  • Aggregates
  • Event handlers
  • Sagas for long-running processes

Axon can be used with Axon Server or integrated into existing messaging platforms like Kafka and RabbitMQ.


Why Use Axon for Microservices?

Traditional microservices often face challenges such as:

  • Tight coupling between services
  • Difficulty managing state transitions
  • Limited traceability of what happened and when

Axon Framework solves this by:

  • Storing domain events instead of mutable state
  • Separating commands, events, and queries
  • Providing auditable history of state changes
  • Encouraging aggregate isolation and decoupling

Setting Up a Java Project with Axon

Add Axon dependencies to your Maven pom.xml:

<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring-boot-starter</artifactId>
<version>4.8.0</version>
</dependency>

Spring Boot will auto-configure command and event buses.


Defining the Domain: Commands, Events, Aggregates

Let’s model a simple Order Service using Axon.

Command:

public class CreateOrderCommand {
@TargetAggregateIdentifier
private final String orderId;
private final String product;

    // Constructor, getters
}

Event:

public class OrderCreatedEvent {
private final String orderId;
private final String product;

    // Constructor, getters
}

Aggregate:

@Aggregate
public class OrderAggregate {

    @AggregateIdentifier
    private String orderId;
    private String product;

    public OrderAggregate() {} // Required by Axon

    @CommandHandler
    public OrderAggregate(CreateOrderCommand command) {
        AggregateLifecycle.apply(new OrderCreatedEvent(command.getOrderId(), command.getProduct()));
    }

    @EventSourcingHandler
    public void on(OrderCreatedEvent event) {
        this.orderId = event.getOrderId();
        this.product = event.getProduct();
    }
}

The aggregate handles commands and rebuilds state using past events.


Handling Events and Projections

Axon supports asynchronous, decoupled event listeners (aka event handlers):

@Component
public class OrderEventHandler {

    @EventHandler
    public void on(OrderCreatedEvent event) {
        // Persist in read model, send notifications, etc.
    }
}

You can build projections (read models) that are optimized for querying.


Implementing CQRS

With Axon, commands mutate state through aggregates, while queries access projections.

Use a dedicated query handler:

@QueryHandler
public List<OrderSummary> handle(FetchAllOrdersQuery query) {
return orderRepository.findAll();
}

This enables high-performance reads and scalable writes.


Axon Server vs External Brokers

You can use:

  • Axon Server (built-in, zero config event store and bus)
  • Kafka / RabbitMQ as message buses
  • JPA / MongoDB for event stores

Axon auto-configures Axon Server if it’s available, or you can customize event storage and routing.


Sagas for Complex Workflows

Use Sagas to manage long-running business processes across aggregates:

@Saga
public class OrderSaga {

    @StartSaga
    @SagaEventHandler(associationProperty = "orderId")
    public void handle(OrderCreatedEvent event) {
        // Trigger payment or shipment
    }
}

Axon manages saga state and correlation automatically.


Benefits and Trade-Offs

Benefits:

  • High scalability
  • Decoupled architecture
  • Auditability
  • Support for eventual consistency

Trade-offs:

  • Learning curve
  • Higher operational complexity
  • Requires careful schema evolution

Conclusion

Axon Framework brings the principles of CQRS, event sourcing, and DDD into your Java microservices without reinventing the wheel. By using commands, events, and aggregates, you get a clean separation of concerns, powerful scalability patterns, and auditability out of the box.

If you’re building resilient, modular, and event-driven systems, Axon is a powerful framework to consider.