Complete CQRS Application with Axon Framework – 2 (Step by Step Guide)

Complete CQRS Application with Axon Framework2

We would now complete our tutorial on CQRS With Axon Framework. Recall that in the first part, we talked about:

In this part we would create the following:

  1. Queries and Interfaces
  2. Components to handle events and queries (query package)
  3. Controllers to handle requests
  4. Watch the Video

Part 1 and Part 3

To begin this part, you  need to create three packages in the solution:

  • controllers: contains the controller classes
  • query: contains the api.kt, the OrderProjector.java and the ProductProjector.java
1. Queries and Interfaces

We would need two queries:

  • one to retrieve a list of orders
  • the other to retrieve a list of products

Both of the queries are defined as data classes.

We also need two repositories that extend the JpaRepository. This would interface with the read model to handle data persistence. They would also be used to handle the queries to retrieved data from the Read DB.

All this would be placed in a kotlin file called api.kt inside the query package.

The content of the api.kt file is given below:

class GetOrdersQuery
class GetProductsQuery

interface OrderSummaryRepository:JpaRepository<OrderSummary, String>
interface ProductSummaryRepository:JpaRepository<ProductSummary, String>

 

2. The Component 

The first component, we’ll call it ProductProjector. This would contain two event handlers: for ProductAdded and StockUpdated events. It would also have one query handler to handle the query to retrieve list of products.

The ProjectProjector file is found here in my github repository. I have decided not to put it here.

Then we would have the OrderProjector.

The OrderProject class would contain the event handler for order created event and the query handler to retrieve a list of orders. You can also get this file from here.

Both the OrderProjector and ProductProjector classes are placed inside the query package just same as the queries and interfaces.

 

3. The RestControllers

I’m sure you already know what the RestController does. This is the class that handles the request coming from the HTTP clients.

So we would have two controllers, both placed inside the controllers package

ProductController: this file contains the methods to add a new product and another method to retrieve a list of products. The content of the ProductController.java class is given below:

@RestController
public class ProductController {

    private CommandGateway commandGateway;
    private QueryGateway queryGateway;

    public ProductController(CommandGateway commandGateway, QueryGateway queryGateway) {
        this.commandGateway = commandGateway;
        this.queryGateway = queryGateway;
    }

    @PostMapping("/addProduct")
    public void handle(@RequestBody ProductSummary summary){
        AddProductCommand cmd = new AddProductCommand(
                summary.getId(),
                summary.getPrice(),
                summary.getStock(),
                summary.getDescription()
        );
        commandGateway.sendAndWait(cmd);
    }

    @GetMapping("/products")
    public CompletableFuture<List<ProductSummary>> getProducts(){
        return queryGateway.query(new GetProductsQuery(), 
ResponseTypes.multipleInstancesOf(ProductSummary.class));
    }
}

Note that the return type of the getProducts function is CompletableFuture<List<ProductSummary>>. This means that QueryGateway’s query method is called asynchronously.

Also note that for the AddProduct, the ProductSummary is retrieved from the RequestBody and used to construct an AddProductCommand. This command is then sent using the Command Gateway

 

OrderController: this file would contain the endpoints for handling create new order requests coming from the client. Also, it would contain a method to return a list of orders by handling the GetOrdersQuery defined in the api.kt file.

The content of the OrderController file is given below:

@RestController
public class OrderController {

    //CommandGateway used CommandBus underneath
    private CommandGateway commandGateway;
    private QueryGateway queryGateway;

    public OrderController(CommandGateway commandGateway, QueryGateway queryGateway) {
        this.commandGateway = commandGateway;
        this.queryGateway = queryGateway;
    }

    @PostMapping("/createOrder")
    public void handle(@RequestBody OrderSummary summary){
        CreateOrderCommand cmd = new CreateOrderCommand(
                summary.getId(),
                summary.getPrice(),
                summary.getNumber(),
                summary.getProductid()
        );
        commandGateway.send(cmd);
    }
    
    @GetMapping("/orders")
    public CompletableFuture<List<OrderSummary>> getOrders(){
        return  queryGateway.query(new GetOrdersQuery(), 
                ResponseTypes.multipleInstancesOf(OrderSummary.class));
    }
}

This is very much similar to the ProductController. Therefore, I will not repeat the explanation. However, I strongly recommend you watch the video to get a clearer understanding of what is going on.