Skip to content

Elma-dev/Ebank_Axon_microsevices_model

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ebank_Axon_microsevices_model

This project explains how to use CQRS and event sourcing in general, as well as how to use Axon with Spring Boot in the microservices space.

 the Axon Framework, a Java framework for building scalable and high-performance event-driven applications. 

📚Prerequisite

Java Spring XAMPP Maven AXON IntelliJ MySQL Swagger

🌳Project Tree

.
├── main
│   ├── java
│   │   └── dev
│   │       └── elma
│   │           └── demo
│   │               ├── DemoApplication.java
│   │               ├── commands
│   │               │   ├── aggregates
│   │               │   │   └── AccountAggregate.java
│   │               │   └── controllers
│   │               │       └── AccountCommandController.java
│   │               ├── commonapi
│   │               │   ├── commands
│   │               │   │   ├── BaseCommand.java
│   │               │   │   ├── CreateAccountCommand.java
│   │               │   │   ├── CreditAccountCommand.java
│   │               │   │   └── DebitAccountCommand.java
│   │               │   ├── dtos
│   │               │   │   ├── CreateAccountCommandDTO.java
│   │               │   │   ├── CreditAccountCommandDTO.java
│   │               │   │   └── DebitAccountCommandDTO.java
│   │               │   ├── enums
│   │               │   │   ├── AccountStatus.java
│   │               │   │   └── TransactionType.java
│   │               │   ├── events
│   │               │   │   ├── AccountCreatedEvent.java
│   │               │   │   ├── AccountCreditedEvent.java
│   │               │   │   ├── AccountDebitedEvent.java
│   │               │   │   └── BaseEvent.java
│   │               │   └── exceptions
│   │               │       └── AccountCommandExceptions.java
│   │               └── queries
│   │                   ├── controllers
│   │                   │   └── QueriesController.java
│   │                   ├── entities
│   │                   │   ├── Account.java
│   │                   │   └── AccountTransaction.java
│   │                   ├── query
│   │                   │   ├── GetAllAcount.java
│   │                   │   ├── GetOne.java
│   │                   │   └── GetTransactionOfAccount.java
│   │                   ├── repositories
│   │                   │   ├── AccountRepository.java
│   │                   │   └── AccountTransactionRepository.java
│   │                   └── services
│   │                       └── AccountEventHandlerServices.java
│   └── resources
│       ├── application.properties
│       ├── static
│       └── templates


Maven Dependencies

Axon Swagger Other

Project: General Schema

image

Class Diagram

image

Commands Building

Commands are a fundamental concept. They are messages that represent requests to change the state of an aggregate (a domain object).

There are 3 commands in our application:
    * Create Account Command
    * Credit Amount to Account Command
    * Debit Amount from Account Command
Also We have Base Command as an abstarct Class

Create Base Class

The @TargetAggregateIdentifier annotation is often used in command handling methods within an aggregate to specify which field within the aggregate represents the target aggregate identifier. This is important for Axon to route commands to the correct aggregate instance.

@AllArgsConstructor
public abstract class BaseCommand<T> {
    @TargetAggregateIdentifier
    @Getter
    private T id;
}

Create Account Command

@Getter
public class CreateAccountCommand extends BaseCommand<String>{
    private BigDecimal balance;
    private String currency;
    public CreateAccountCommand(String id,BigDecimal balance,String currency) {
        super(id);
        this.balance=balance;
        this.currency=currency;
    }
}

Create Debit Command

@Getter
public class CreditAccountCommand extends BaseCommand<String>{
    private BigDecimal amount;
    private String currency;
    public CreditAccountCommand(String accountId, BigDecimal amount,String currency) {
        super(accountId);
        this.amount=amount;
        this.currency=currency;
    }
}

Create Credit Command

@Getter
public class DebitAccountCommand extends BaseCommand<String>{
    private BigDecimal amount;
    private String currency;
    public DebitAccountCommand(String accountId, BigDecimal amount, String currency) {
        super(accountId);
        this.amount=amount;
        this.currency=currency;
    }
}

Event Building

Events are closely related to commands, as mentioned in the previous response. While commands represent requests to perform actions, events represent the results or consequences of those actions.

There are 3 events in our application:
    * Create Account event
    * Credit Amount to Account event
    * Debit Amount from Account event
Also We have Base Event as an abstarct Class

Create Base Event

public abstract class BaseEvent<T> {
    @Getter private T id;
    public BaseEvent(T id) {
        this.id = id;
    }
}

Create Account Event

@Getter
public class AccountCreatedEvent extends BaseEvent<String>{
    private BigDecimal balance;
    private String currency;
    private AccountStatus status;
    public AccountCreatedEvent(String id, BigDecimal balance, String currency, AccountStatus status){
        super(id);
        this.balance=balance;
        this.currency=currency;
        this.status=status;
    }
}

Create Credit Event

@Getter
public class AccountCreditedEvent extends BaseEvent<String>{
    private BigDecimal amount;
    private String currency;
    public AccountCreditedEvent(String id, BigDecimal amount,String currency){
        super(id);
        this.amount=amount;
        this.currency=currency;
    }
}

Create Debit Event

@Getter
public class AccountDebitedEvent extends BaseEvent<String>{
    private BigDecimal amount;
    private String currency;
    public AccountDebitedEvent(String id, BigDecimal amount,String currency){
        super(id);
        this.amount=amount;
        this.currency=currency;
    }
}

Aggregate

aggregate is a fundamental concept that represents a cluster of related domain objects and encapsulates the business logic and state for a specific part of the domain.

@Aggregate //axonAggregate
public class AccountAggregate {
    @AggregateIdentifier
    private String id;
    private BigDecimal balance;
    private String currency;
    private AccountStatus status;
    protected AccountAggregate() {
        //Required By Axon
    }

    @CommandHandler
    public AccountAggregate(CreateAccountCommand command) {
        //Decision Functions Here
        if(command.getBalance().doubleValue()<=0) throw new AccountCommandExceptions("Negative Balance");
        // When all commands are acceptable then we transfer them  to be events and save it in events store
        AccountCreatedEvent event = new AccountCreatedEvent(command.getId(), command.getBalance(), command.getCurrency(), AccountStatus.CREATED);
        AggregateLifecycle.apply(event); // publish events
    }
    //Now we create an event handler whose update the state of our account (application)
    @EventSourcingHandler
    public void on(AccountCreatedEvent event){
        this.id=event.getId();
        this.balance=event.getBalance();
        this.currency=event.getCurrency();
        this.status=event.getStatus();
    }
    @CommandHandler
    public void handle(DebitAccountCommand command){
        if(this.balance.doubleValue()<command.getAmount().doubleValue()) throw new AccountCommandExceptions("Balance insufficient");
        AccountDebitedEvent accountDebitedEvent = new AccountDebitedEvent(command.getId().toString(), command.getAmount(), command.getCurrency());
        AggregateLifecycle.apply(accountDebitedEvent);

    }
    @EventSourcingHandler
    public void on(AccountDebitedEvent event){
        this.balance=BigDecimal.valueOf(this.balance.doubleValue()-event.getAmount().doubleValue());
    }
    @CommandHandler
    public void handle(CreditAccountCommand command){
        if(command.getAmount().doubleValue()<=0) throw new AccountCommandExceptions("Negative Amount");
        AggregateLifecycle.apply(
                new AccountCreditedEvent(
                        command.getId(),
                        command.getAmount(),
                        command.getCurrency()
                )
        );

    }
    @EventSourcingHandler
    public void on(AccountCreditedEvent event){
        this.balance=BigDecimal.valueOf(this.balance.doubleValue()+event.getAmount().doubleValue());
    }
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages