Reading Time: 5 minutes







In Part 1 (this post), we will continue with our implementation and connect our services to the Axon Server.
Common Service Implementation:
Structure of Common Service:

CancelOrderCommand Class:
package com.knoldus.commonservice.commands;
import lombok.Value;
import org.axonframework.modelling.command.TargetAggregateIdentifier;
@Value
public class CancelOrderCommand {
@TargetAggregateIdentifier
private String orderId;
private String orderStatus = "CANCELLED";
}
CancelPaymentCommand class :
package com.knoldus.commonservice.commands;
import lombok.Value;
import org.axonframework.modelling.command.TargetAggregateIdentifier;
@Value
public class CancelPaymentCommand {
@TargetAggregateIdentifier
private String paymentId;
private String orderId;
private String paymentStatus = "CANCELLED";
}
CompleteOrderCommand class:
package com.knoldus.commonservice.commands;
import lombok.Builder;
import lombok.Data;
import org.axonframework.modelling.command.TargetAggregateIdentifier;
@Data
@Builder
public class CompleteOrderCommand {
@TargetAggregateIdentifier
private String orderId;
private String orderStatus;
}
ShipOrderCommand :
package com.knoldus.commonservice.commands;
import lombok.Builder;
import lombok.Data;
import org.axonframework.modelling.command.TargetAggregateIdentifier;
@Data
@Builder
public class ShipOrderCommand {
@TargetAggregateIdentifier
private String shipmentId;
private String orderId;
}
ValidatePaymentCommand :
package com.knoldus.commonservice.commands;
import com.knoldus.commonservice.model.CardDetails;
import lombok.Builder;
import lombok.Data;
import org.axonframework.modelling.command.TargetAggregateIdentifier;
@Data
@Builder
public class ValidatePaymentCommand {
@TargetAggregateIdentifier
private String paymentId;
private String orderId;
private CardDetails cardDetails;
}
OrderCancelledEvent :
package com.knoldus.commonservice.events;
import lombok.Data;
@Data
public class OrderCancelledEvent {
private String orderId;
private String orderStatus;
}
OrderCompletedEvent Class :
package com.knoldus.commonservice.events;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class OrderCompletedEvent {
private String orderId;
private String orderStatus;
}
OrderShippedEvent class :
package com.knoldus.commonservice.events;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class OrderShippedEvent {
private String shipmentId;
private String orderId;
private String shipmentStatus;
}
PaymentCancelledEvent Class :
package com.knoldus.commonservice.events;
import lombok.Data;
@Data
public class PaymentCancelledEvent {
private String paymentId;
private String orderId;
private String paymentStatus;
}
PaymentProcessedEvent Class:
package com.knoldus.commonservice.events;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PaymentProcessedEvent {
private String paymentId;
private String orderId;
}
Card Details Class :
package com.knoldus.commonservice.model;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class CardDetails {
private String name;
private String cardNumber;
private Integer validUntilMonth;
private Integer validUntilYear;
private Integer cvv;
}
User Detail Class :
package com.knoldus.commonservice.model;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class User {
private String userId;
private String firstName;
private String lastName;
private CardDetails cardDetails;
}
GetUserPaymentDetailsQuery Class :
package com.knoldus.commonservice.queries;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GetUserPaymentDetailsQuery {
private String userId;
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knoldus</groupId>
<artifactId>common-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>CommonService</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--
https://mvnrepository.com/artifact/org.axonframework/axon-spring-boot-starter
-->
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring-boot-starter</artifactId>
<version>4.5.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-
plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Payment Service Implementation:
Payment Service Structure :



Payment Aggregate: This service will receive the event and process it accordingly.
PaymentAggregate Class :
package com.knoldus.paymentservice.command.api.aggregate;
import com.knoldus.commonservice.commands.CancelPaymentCommand;
import com.knoldus.commonservice.commands.ValidatePaymentCommand;
import com.knoldus.commonservice.events.PaymentCancelledEvent;
import com.knoldus.commonservice.events.PaymentProcessedEvent;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.eventsourcing.EventSourcingHandler;
import org.axonframework.modelling.command.AggregateIdentifier;
import org.axonframework.modelling.command.AggregateLifecycle;
import org.axonframework.spring.stereotype.Aggregate;
import org.springframework.beans.BeanUtils;
@Aggregate
@Slf4j
public class PaymentAggregate {
@AggregateIdentifier
private String paymentId;
private String orderId;
private String paymentStatus;
public PaymentAggregate() {
}
@CommandHandler
public PaymentAggregate(ValidatePaymentCommand validatePaymentCommand) {
//Validate the Payment Details
// Publish the Payment Processed event
log.info("Executing ValidatePaymentCommand for " +
"Order Id: {} and Payment Id: {}",
validatePaymentCommand.getOrderId(),
validatePaymentCommand.getPaymentId());
PaymentProcessedEvent paymentProcessedEvent
= new PaymentProcessedEvent(
validatePaymentCommand.getPaymentId(),
validatePaymentCommand.getOrderId()
);
AggregateLifecycle.apply(paymentProcessedEvent);
log.info("PaymentProcessedEvent Applied");
}
@EventSourcingHandler
public void on(PaymentProcessedEvent event) {
this.paymentId = event.getPaymentId();
this.orderId = event.getOrderId();
}
@CommandHandler
public void handle(CancelPaymentCommand cancelPaymentCommand) {
PaymentCancelledEvent paymentCancelledEvent
= new PaymentCancelledEvent();
BeanUtils.copyProperties(cancelPaymentCommand,
paymentCancelledEvent);
AggregateLifecycle.apply(paymentCancelledEvent);
}
@EventSourcingHandler
public void on(PaymentCancelledEvent event) {
this.paymentStatus = event.getPaymentStatus();
}
}
Payment Class :
package com.knoldus.paymentservice.command.api.data;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Date;
@Data
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Payment {
@Id
private String paymentId;
private String orderId;
private Date timeStamp;
private String paymentStatus;
}
package com.knoldus.paymentservice.command.api.data;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PaymentRepository extends JpaRepository<Payment, String> {
}
PaymentsEventHandler Class :
package com.knoldus.paymentservice.command.api.events;
import com.knoldus.commonservice.events.PaymentCancelledEvent;
import com.knoldus.commonservice.events.PaymentProcessedEvent;
import com.knoldus.paymentservice.command.api.data.Payment;
import com.knoldus.paymentservice.command.api.data.PaymentRepository;
import org.axonframework.eventhandling.EventHandler;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class PaymentsEventHandler {
private PaymentRepository paymentRepository;
public PaymentsEventHandler(PaymentRepository paymentRepository) {
this.paymentRepository = paymentRepository;
}
@EventHandler
public void on(PaymentProcessedEvent event) {
Payment payment
= Payment.builder()
.paymentId(event.getPaymentId())
.orderId(event.getOrderId())
.paymentStatus("COMPLETED")
.timeStamp(new Date())
.build();
paymentRepository.save(payment);
}
@EventHandler
public void on(PaymentCancelledEvent event) {
Payment payment
= paymentRepository.findById(event.getPaymentId()).get();
payment.setPaymentStatus(event.getPaymentStatus());
paymentRepository.save(payment);
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knoldus</groupId>
<artifactId>payment-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>PaymentService</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--
https://mvnrepository.com/artifact/org.axonframework/axon-spring-boot-starter
-->
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring-boot-starter</artifactId>
<version>4.5.3</version>
</dependency>
<!--
https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
<dependency>
<groupId>com.knoldus</groupId>
<artifactId>common-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-
plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Shipment Service Implementation:
Shipment Service Structure :



ShipmentAggregate: Will handle the event and process the incoming event.
ShipmentAggregate Class :
package com.knoldus.shipmentservice.command.api.aggregate;
import com.knoldus.commonservice.commands.ShipOrderCommand;
import com.knoldus.commonservice.events.OrderShippedEvent;
import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.eventsourcing.EventSourcingHandler;
import org.axonframework.modelling.command.AggregateIdentifier;
import org.axonframework.modelling.command.AggregateLifecycle;
import org.axonframework.spring.stereotype.Aggregate;
@Aggregate
public class ShipmentAggregate {
@AggregateIdentifier
private String shipmentId;
private String orderId;
private String shipmentStatus;
public ShipmentAggregate() {
}
@CommandHandler
public ShipmentAggregate(ShipOrderCommand shipOrderCommand) {
//Validate the Command
// Publish the Order Shipped event
OrderShippedEvent orderShippedEvent
= OrderShippedEvent
.builder()
.shipmentId(shipOrderCommand.getShipmentId())
.orderId(shipOrderCommand.getOrderId())
.shipmentStatus("COMPLETED")
.build();
AggregateLifecycle.apply(orderShippedEvent);
}
@EventSourcingHandler
public void on(OrderShippedEvent event) {
this.orderId = event.getOrderId();
this.shipmentId = event.getShipmentId();
this.shipmentStatus = event.getShipmentStatus();
}
}
Shipment Class :
package com.knoldus.shipmentservice.command.api.data;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
@Data
@Entity
public class Shipment {
@Id
private String shipmentId;
private String orderId;
private String shipmentStatus;
}
ShipmentRepository Class :
package com.knoldus.shipmentservice.command.api.data;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ShipmentRepository extends JpaRepository<Shipment, String> {
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knoldus</groupId>
<artifactId>shipment-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>PaymentService</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--
https://mvnrepository.com/artifact/org.axonframework/axon-spring-boot-starter
-->
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring-boot-starter</artifactId>
<version>4.5.3</version>
</dependency>
<!--
https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
<dependency>
<groupId>com.knoldus</groupId>
<artifactId>common-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.properties
server.port = 9094
spring.datasource.url=jdbc:h2:file:~/data/shipmentDB
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto =update
spring.h2.console.settings.web-allow-others=true
docker-compose.yml
version: "3.2"
services:
axonserver:
image: axoniq/axonserver
hostname: axonserver
ports:
- '8024:8024'
- '8124:8124'
- '8224:8224'