July 15, 2026
CQRS and Event Sourcing application architecture components diagram

CQRS and Event Sourcing with Axon Framework — Complete Guide (2026)

Expanded guide — June 29, 2026 · CQRS, Event Sourcing, and Axon Framework with Spring Boot code samples. Microservices tutorials hub

Hands-on tutorial: Axon Framework CQRS Tutorial — Step by Step (2026)

Kindson Munonye · Software engineer & technical author
GitHub · LinkedIn · About · YouTube
Last updated by Kindson Munonye — June 29, 2026


📚 Tutorial hub: Microservices & CQRS tutorials · CRUD with Spring Boot backend

Prerequisites: Java, Spring Boot basics, REST APIs. Familiarity with Spring Boot CRUD helps.

Estimated reading time: ~20 minutes · Last updated: June 29, 2026

Video companion: CQRS & Event Sourcing explained (YouTube)

📚 Browse all tutorials: Microservices & CQRS tutorials hub · CRUD with Spring Boot backend

In this simple article, I would tell you all the components that make up a CQRS and Event Sourcing application. So if you know how all this files hook together, then you can build any application using the CQRS pattern.

Scenario: Order Placement Application

A user places an order(OrderCreatedCommand) via a UI application built in Angular(step by step here). An order is created with the quantity provided by the user and the item selected by the user. Once this order is created, a StockUpdated event is published which updates the product stock by depreciating it by the amount in the order. User is able to see list of orders and list of products on the UI as well.

Complete applications on Github

Angular UI app

Order processing app with Axon

All the Component

Now we would partition the component into three groups:

  1. API/Command Components
  2. Query Components
  3. Common Components
  4. Additional CQRS and Event Sourcing Resources

We also have a 4th group,  the Events components, which are spread through all the other components. Also note that there are some overlap between these components. Let’s now discuss these components;

1.  API/Command Components

These are components involved in the creation and handling of commands. I have used some codes to represent each component so that later, I would explain how they interact. The Aggregates are what is stored in the write store(remember in CQRS, there is both read store and write store). Commands are operations that need to be performed. In this case there are three commands for: adding a product, creating an order and updating the product stock. You can add more. There are also three events, command handler and event-sourcing handlers. Event-sourcing is simply a way to save and retrieve data from the write store.

Figure 1: API/Commands Components of CQRS and ES
Figure 1: API/Commands Components of CQRS and ES

2.  Query Components

These components are responsible for creation and response to queries. I have outlined three queries, but it can be more. The Read Model and Read Repositories is exactly the same as in normal ORM(Object Relational Mapping). The ProductView and OrderView are entities while the ProductRepository and OrderRepository are Jpa or Crud repository interface. Finally, for all the queries, there must be a query handler. Four of them are provides.

CQRS - Query Components
Figure 3 – Query Components of CQRS

3.  Common Components

The common components are involved in routing messages between various parts of the architecture. There are three gateways: the CommandGateway sends new commands into the command bus; the QueryGateway queries new queries into the query bus; the EventGateway publishes new events to the event bus. Also the buses are like channels to carry messages across:

  • Command Bus: a channel for commands moving from the gateway to the command handler
  • Event Bus: a channel to move events from the gateway to the event handler
  • Query Bus: a channel to move queries from the gateway to the query handler
Figure 3 - Common Components of CQRS
Figure 3 – Common Components of CQRS

I’ll recommend you follow my step by step to build the application so you can see how all these artefacts interact. Finally, I would like to differentiate between and EventHandler and an EventSourcingHandlers.

Both EventHandler and EventSourcingHandler responds to events. However, EventSourcingHandlers are concerned with reading and writing aggregates to the write store. Therefore, changes made to the state of an aggregate are persisted in the write store by the EventSourcingHandler. EventHandler on the other hands, can be made to perform a wide range of actions depending on the application requirements.

Additional CQRS and Event Sourcing Resources

Axon Framework Overview

Axon Framework is the most popular Java library for CQRS and Event Sourcing. It provides:

  • Command handlers — process write operations (CreateOrder, UpdateInventory)
  • Event handlers — react to domain events and update read models
  • Aggregate roots — enforce business rules and emit events
  • Event store — persist events (Axon Server or embedded)

Axon separates the command side (writes) from the query side (reads), which is the core of CQRS.

Setting Up Axon Server

Download and run Axon Server (local dev):

# Download from https://axoniq.io/product-overview/axon-server
# Run standalone
./axonserver

# Default ports: 8024 (HTTP), 8124 (gRPC)

Add Axon dependencies to your Spring Boot pom.xml:

<dependency>
  <groupId>org.axonframework</groupId>
  <artifactId>axon-spring-boot-starter</artifactId>
  <version>4.9.3</version>
</dependency>
<dependency>
  <groupId>org.axonframework</groupId>
  <artifactId>axon-server-connector</artifactId>
  <version>4.9.3</version>
</dependency>

Configure application.properties:

axon.axonserver.servers=localhost:8124
spring.application.name=order-service

Command Side — Aggregate and Command Handler

Define a command and an aggregate that applies events:

// CreateOrderCommand.java
public class CreateOrderCommand {{
  @TargetAggregateIdentifier
  private final String orderId;
  private final String productId;
  public CreateOrderCommand(String orderId, String productId) {{
    this.orderId = orderId; this.productId = productId;
  }}
}}

// OrderAggregate.java
@Aggregate
public class OrderAggregate {{
  @AggregateIdentifier private String orderId;

  protected OrderAggregate() {{}}

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

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

Event Handler — Updating the Read Model

Event handlers project events into a query-optimized database (the “Q” in CQRS):

@Component
public class OrderProjection {{
  @Autowired private OrderRepository orderRepository;

  @EventHandler
  public void on(OrderCreatedEvent event) {{
    orderRepository.save(new OrderView(event.getOrderId(), event.getProductId(), "CREATED"));
  }}
}}

The read model can use a different schema, database, or even Elasticsearch — optimized for queries without affecting the write model.

Query Side — REST Controller

@RestController
@RequestMapping("/orders")
public class OrderQueryController {{
  @Autowired private OrderRepository orderRepository;

  @GetMapping("/{id}")
  public OrderView getOrder(@PathVariable String id) {{
    return orderRepository.findById(id).orElseThrow();
  }}
}}

Commands go through Axon (CommandGateway); queries hit the read model directly — this is CQRS in practice.

CQRS vs Traditional CRUD

Your Angular CRUD tutorial uses a single REST model for reads and writes. CQRS splits these when:

  • Read and write patterns differ significantly (e.g. complex reports vs simple commands)
  • You need a full audit trail via event sourcing
  • Multiple services consume the same event stream

Next Steps

  1. Run Axon Server locally and connect your Spring Boot app
  2. Implement one aggregate with two events (Create + Update)
  3. Add a projection that builds a query table
  4. Explore the Microservices tutorials hub for related architecture guides

Additional CQRS and Event Sourcing Resources

Event Sourcing in Depth

In event sourcing, the event store is the source of truth — not the current row in a database table. To rebuild state, replay events:

  1. OrderCreatedEvent → order status = CREATED
  2. OrderShippedEvent → order status = SHIPPED
  3. OrderDeliveredEvent → order status = DELIVERED

Axon replays events into aggregates automatically via @EventSourcingHandler. Snapshots can speed up replay for aggregates with thousands of events.

Sagas and Process Managers

When one command triggers workflows across multiple aggregates (place order → reserve inventory → charge payment), use Axon Sagas:

@Saga
public class OrderPlacementSaga {{
  @StartSaga
  @SagaEventHandler(associationProperty = "orderId")
  public void on(OrderCreatedEvent event) {{
    // send ReserveInventoryCommand
  }}

  @SagaEventHandler(associationProperty = "orderId")
  public void on(InventoryReservedEvent event) {{
    // send ProcessPaymentCommand
  }}
}}

Sagas coordinate long-running business processes without tight coupling between services.

Spring Boot + Axon Integration Tips

  • Enable Axon with @EnableAxon on your Spring Boot application class
  • Inject CommandGateway in REST controllers to dispatch commands
  • Use @ProcessingGroup on event handlers for parallel processing
  • Configure separate data sources for event store vs read model if needed
@RestController
public class OrderCommandController {{
  @Autowired CommandGateway commandGateway;

  @PostMapping("/orders")
  public CompletableFuture<String> create(@RequestBody CreateOrderRequest req) {{
    String id = UUID.randomUUID().toString();
    return commandGateway.send(new CreateOrderCommand(id, req.getProductId()));
  }}
}}

CQRS in Microservices Architecture

In a microservices system, each bounded context can have its own command model and one or more read models:

  • Order Service — commands: CreateOrder, CancelOrder; events: OrderCreated, OrderCancelled
  • Inventory Service — listens to OrderCreated, emits InventoryReserved
  • Reporting Service — builds denormalized views from event stream

Compare this to the monolithic CRUD approach in our Angular + Spring Boot CRUD series — CQRS adds complexity but scales reads and writes independently.

Testing CQRS Components

// Unit test aggregate with Axon Test fixture
@Test
void shouldCreateOrder() {{
  fixture.givenNoPriorActivity()
    .when(new CreateOrderCommand("id-1", "product-1"))
    .expectEvents(new OrderCreatedEvent("id-1", "product-1"));
}}

Axon provides AggregateTestFixture and SagaTestFixture for fast, in-memory testing without Axon Server.

Common CQRS Mistakes to Avoid

  • Using CQRS for simple CRUD apps (unnecessary complexity)
  • Sharing the same database schema for commands and queries (defeats the purpose)
  • Not handling eventual consistency on the read side (UI must tolerate delay)
  • Skipping event versioning strategy (events live forever)




Kindson Munonye

Kindson Munonye is a software engineer and technical author specializing in Angular, Spring Boot, and microservices architecture. He publishes step-by-step tutorials with source code covering CRUD operations, reactive forms, CQRS, event sourcing, and REST API integration.GitHub · LinkedIn · About · YouTube

View all posts by Kindson Munonye →