Course – Black Friday 2025 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our Black Friday Sale. All Access and Pro are 33% off until 2nd December, 2025:

>> EXPLORE ACCESS NOW

Partner – Orkes – NPI EA (cat=Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag=Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Partner – Orkes – NPI EA (cat=Java)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – Black Friday 2025 – NPI (cat=Baeldung)
announcement - icon

Yes, we're now running our Black Friday Sale. All Access and Pro are 33% off until 2nd December, 2025:

>> EXPLORE ACCESS NOW

1. Overview

In this article, we’ll be looking at Axon and how it helps us implement applications with CQRS (Command Query Responsibility Segregation) and Event Sourcing in mind.

During this guide, both Axon Framework and Axon Server will be utilized. The former will contain our implementation and the latter will be our dedicated Event Store and Message Routing solution.

The sample application we’ll be building focuses on an Order domain. For this, we’ll be leveraging the CQRS and Event Sourcing building blocks Axon provides us.

Note that a lot of the shared concepts come right out of DDD, which is beyond the scope of this current article.

2. Maven Dependencies

We’ll create an Axon / Spring Boot application. Hence, we need to add the latest axon-spring-boot-starter dependency to our pom.xml, as well as the axon-test dependency for testing.
To use matching versions, we will use the axon-bom within our dependency management section:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.axonframework</groupId>
            <artifactId>axon-bom</artifactId>
            <version>4.9.3</version>
        </dependency>
    </dependencies>
</dependencyManagement>

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

    <dependency>
        <groupId>org.axonframework</groupId>
        <artifactId>axon-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3. Axon Server

We will use Axon Server to be our Event Store and our dedicated command, event and query routing solution.

As an Event Store, it gives us the ideal characteristics required when storing events.

As a Message Routing solution, it gives us the option to connect several instances together without focusing on configuring things like a RabbitMQ or a Kafka topic to share and dispatch messages.

Axon Server can be downloaded here. As it is a simple JAR file, the following operation suffices to start it up:

java -jar axonserver.jar

This will start a single Axon Server instance which is accessible through localhost:8024. The endpoint provides an overview of the connected applications and the messages they can handle, as well as a querying mechanism towards the Event Store contained within Axon Server.

The default configuration of Axon Server together with the axon-spring-boot-starter dependency will ensure our Order service will automatically connect to it.

4. Order Service API – Commands

We’ll set up our Order service with CQRS in mind. Therefore we’ll emphasize the messages that flow through our application.

First, we’ll define the Commands, meaning the expressions of intent. The Order service is capable of handling three different types of actions:

  1. Creating a new order
  2. Confirming an order
  3. Shipping an order

Naturally, there will be three command messages that our domain can deal with — CreateOrderCommand, ConfirmOrderCommand, and ShipOrderCommand:

public class CreateOrderCommand {
 
    @TargetAggregateIdentifier
    private final String orderId;
    private final String productId;
 
    // constructor, getters, equals/hashCode and toString 
}
public class ConfirmOrderCommand {
 
    @TargetAggregateIdentifier
    private final String orderId;
    
    // constructor, getters, equals/hashCode and toString
}
public class ShipOrderCommand {
 
    @TargetAggregateIdentifier
    private final String orderId;
 
    // constructor, getters, equals/hashCode and toString
}

The TargetAggregateIdentifier annotation tells Axon that the annotated field is an id of a given aggregate to which the command should be targeted. We’ll briefly touch on aggregates later in this article.

Also, note that we marked the fields in the commands as final. This is intentional, as it’s a best practice for any message implementation to be immutable.

5. Order Service API – Events

Our aggregate will handle the commands, as it’s in charge of deciding if an Order can be created, confirmed, or shipped.

It will notify the rest of the application of its decision by publishing an event. We’ll have three types of events — OrderCreatedEvent, OrderConfirmedEvent, and OrderShippedEvent:

public class OrderCreatedEvent {
 
    private final String orderId;
    private final String productId;
 
    // default constructor, getters, equals/hashCode and toString
}
public class OrderConfirmedEvent {
 
    private final String orderId;
 
    // default constructor, getters, equals/hashCode and toString
}
public class OrderShippedEvent { 

    private final String orderId; 

    // default constructor, getters, equals/hashCode and toString 
}

6. The Command Model – Order Aggregate

Now that we’ve modeled our core API with respect to the commands and events, we can start creating the Command Model.

The Aggregate is a regular component within the Command Model and stems from DDD. Other frameworks use the concept too, as is for example seen in this article about persisting DDD aggregates with Spring.

As our domain focuses on dealing with Orders, we’ll create an OrderAggregate as the centre of our Command Model.

6.1. Aggregate Class

Thus, let’s create our basic aggregate class:

@Aggregate
public class OrderAggregate {

    @AggregateIdentifier
    private String orderId;
    private boolean orderConfirmed;

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

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

    protected OrderAggregate() { }
}

The Aggregate annotation is an Axon Spring specific annotation marking this class as an aggregate. It will notify the framework that the required CQRS and Event Sourcing specific building blocks need to be instantiated for this OrderAggregate.

As an aggregate will handle commands that are targeted to a specific aggregate instance, we need to specify the identifier with the AggregateIdentifier annotation.

Our aggregate will commence its life cycle upon handling the CreateOrderCommand in the OrderAggregate ‘command handling constructor’. To tell the framework that the given function is able to handle commands, we’ll add the CommandHandler annotation.

When handling the CreateOrderCommand, it will notify the rest of the application that an order was created by publishing the OrderCreatedEvent. To publish an event from within an aggregate, we’ll use AggregateLifecycle#apply(Object…).

From this point, we can actually start to incorporate Event Sourcing as the driving force to recreate an aggregate instance from its stream of events.

We start this off with the ‘aggregate creation event’, the OrderCreatedEvent, which is handled in an EventSourcingHandler annotated function to set the orderId and orderConfirmed state of the Order aggregate.

Also note that to be able to source an aggregate based on its events, Axon requires a default constructor.

6.2. Aggregate Command Handlers

Now that we have our basic aggregate, we can start implementing the remaining command handlers:

@CommandHandler 
public void handle(ConfirmOrderCommand command) { 
    if (orderConfirmed) {
        return;
    }
    apply(new OrderConfirmedEvent(orderId)); 
} 

@CommandHandler 
public void handle(ShipOrderCommand command) { 
    if (!orderConfirmed) { 
        throw new UnconfirmedOrderException(); 
    } 
    apply(new OrderShippedEvent(orderId)); 
} 

@EventSourcingHandler 
public void on(OrderConfirmedEvent event) { 
    orderConfirmed = true; 
}

The signature of our command and event sourcing handlers simply states handle({the-command}) and on({the-event}) to maintain a concise format.

Additionally, we’ve defined that an Order can only be confirmed once and shipped if it has been confirmed. Thus, we’ll ignore the command in the former, and throw an UnconfirmedOrderException if the latter isn’t the case.

This exemplifies the need for the OrderConfirmedEvent sourcing handler to update the orderConfirmed state to true for the Order aggregate.

7. Testing the Command Model

First, we need to set up our test by creating a FixtureConfiguration for the OrderAggregate:

private FixtureConfiguration<OrderAggregate> fixture;

@Before
public void setUp() {
    fixture = new AggregateTestFixture<>(OrderAggregate.class);
}

The first test case should cover the simplest situation. When the aggregate handles the CreateOrderCommand, it should produce an OrderCreatedEvent:

String orderId = UUID.randomUUID().toString();
String productId = "Deluxe Chair";
fixture.givenNoPriorActivity()
  .when(new CreateOrderCommand(orderId, productId))
  .expectEvents(new OrderCreatedEvent(orderId, productId));

Next, we can test the decision-making logic of only being able to ship an Order if it’s been confirmed. Due to this, we have two scenarios — one where we expect an exception, and one where we expect an OrderShippedEvent.

Let’s take a look at the first scenario, where we expect an exception:

String orderId = UUID.randomUUID().toString();
String productId = "Deluxe Chair";
fixture.given(new OrderCreatedEvent(orderId, productId))
  .when(new ShipOrderCommand(orderId))
  .expectException(UnconfirmedOrderException.class);

And now the second scenario, where we expect an OrderShippedEvent:

String orderId = UUID.randomUUID().toString();
String productId = "Deluxe Chair";
fixture.given(new OrderCreatedEvent(orderId, productId), new OrderConfirmedEvent(orderId))
  .when(new ShipOrderCommand(orderId))
  .expectEvents(new OrderShippedEvent(orderId));

8. The Query Model – Event Handlers

So far, we’ve established our core API with the commands and events, and we have the command model of our CQRS Order service, the OrderAggregate, in place.

Next, we can start thinking of one of the Query Models our application should service.

One of these models is the Order:

public class Order {

    private final String orderId;
    private final String productId;
    private OrderStatus orderStatus;

    public Order(String orderId, String productId) {
        this.orderId = orderId;
        this.productId = productId;
        orderStatus = OrderStatus.CREATED;
    }

    public void setOrderConfirmed() {
        this.orderStatus = OrderStatus.CONFIRMED;
    }

    public void setOrderShipped() {
        this.orderStatus = OrderStatus.SHIPPED;
    }

    // getters, equals/hashCode and toString functions
}
public enum OrderStatus {
    CREATED, CONFIRMED, SHIPPED
}

We’ll update this model based on the events propagating through our system. A Spring Service bean to update our model will do the trick:

@Service
public class OrdersEventHandler {

    private final Map<String, Order> orders = new HashMap<>();

    @EventHandler
    public void on(OrderCreatedEvent event) {
        String orderId = event.getOrderId();
        orders.put(orderId, new Order(orderId, event.getProductId()));
    }

    // Event Handlers for OrderConfirmedEvent and OrderShippedEvent...
}

As we’ve used the axon-spring-boot-starter dependency to initiate our Axon application, the framework will automatically scan all the beans for existing message-handling functions.

As the OrdersEventHandler has EventHandler annotated functions to store an Order and update it, this bean will be registered by the framework as a class that should receive events without requiring any configuration on our part.

9. The Query Model – Query Handlers

Next, to query this model to for example retrieve all the orders, we should first introduce a Query message to our core API:

public class FindAllOrderedProductsQuery { }

Second, we’ll have to update the OrdersEventHandler to be able to handle the FindAllOrderedProductsQuery:

@QueryHandler
public List<Order> handle(FindAllOrderedProductsQuery query) {
    return new ArrayList<>(orders.values());
}

The QueryHandler annotated function will handle the FindAllOrderedProductsQuery and is set to return a List<Order> regardless, similarly to any ‘find all’ query.

10. Putting Everything Together

We’ve fleshed out our core API with commands, events, and queries, and set up our command and query model by having an OrderAggregate and Order model.

Next is to tie up the loose ends of our infrastructure. As we’re using the axon-spring-boot-starter, this sets a lot of the required configuration automatically.

First, as we want to leverage Event Sourcing for our Aggregate, we’ll need an EventStore. Axon Server which we have started up in step three will fill this hole.

Secondly, we need a mechanism to store our Order query model. For this example, we can add h2 as an in-memory database and spring-boot-starter-data-jpa for ease of use:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

10.1. Setting up a REST Endpoint

Next, we need to be able to access our application, for which we’ll be leveraging a REST endpoint by adding the spring-boot-starter-web dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

From our REST endpoint, we can start dispatching commands and queries:

@RestController
public class OrderRestEndpoint {

    private final CommandGateway commandGateway;
    private final QueryGateway queryGateway;

    // Autowiring constructor and POST/GET endpoints
}

The CommandGateway is used as the mechanism to send our command messages, and the QueryGateway, in turn, sends query messages. The gateways provide a simpler, more straightforward API, compared to the CommandBus and QueryBus that they connect with.

From here on, our OrderRestEndpoint should have a POST endpoint to create, confirm, and ship an order:

@PostMapping("/ship-order")
public CompletableFuture<Void> shipOrder() {
    String orderId = UUID.randomUUID().toString();
    return commandGateway.send(new CreateOrderCommand(orderId, "Deluxe Chair"))
                         .thenCompose(result -> commandGateway.send(new ConfirmOrderCommand(orderId)))
                         .thenCompose(result -> commandGateway.send(new ShipOrderCommand(orderId)));
}

This rounds up the command side of our CQRS application. Note that a CompletableFuture is returned by the gateway, enabling asynchronizity.

Now, all that’s left is a GET endpoint to query all the Order:

@GetMapping("/all-orders")
public CompletableFuture<List<Order>> findAllOrders() {
    return queryGateway.query(new FindAllOrderedProductsQuery(), ResponseTypes.multipleInstancesOf(Order.class));
}

In the GET endpoint, we leverage the QueryGateway to dispatch a point-to-point query. In doing so, we create a default FindAllOrderedProductsQuery, but we also need to specify the expected return type.

As we expect multiple Order instances to be returned, we leverage the static ResponseTypes#multipleInstancesOf(Class) function. With this, we have provided a basic entrance into the query side of our Order service.

We completed the setup, so now we can send some commands and queries through our REST Controller once we’ve started up the OrderApplication.

POST-ing to endpoint /ship-order will instantiate an OrderAggregate that’ll publish events, which, in turn, will save/update our Orders. GET-ing from the /all-orders endpoint will publish a query message that’ll be handled by the OrdersEventHandler, which will return all the existing Orders.

11. Conclusion

In this article, we introduced the Axon Framework as a powerful base for building an application leveraging the benefits of CQRS and Event Sourcing.

We implemented a simple Order service using the framework to show how such an application should be structured in practice.

Lastly, Axon Server posed as our Event Store and the message routing mechanism, greatly simplifying the infrastructure.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.

For any additional questions on this topic, also check out Discuss AxonIQ.

Course – Black Friday 2025 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our Black Friday Sale. All Access and Pro are 33% off until 2nd December, 2025:

>> EXPLORE ACCESS NOW

Partner – Orkes – NPI EA (cat = Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag = Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – Black Friday 2025 – NPI (All)
announcement - icon

Yes, we're now running our Black Friday Sale. All Access and Pro are 33% off until 2nd December, 2025:

>> EXPLORE ACCESS NOW

eBook Jackson – NPI EA – 3 (cat = Jackson)