Giftcard demo understanding

Hi, I have recently been learning about cqrs/event sourcing and using the axon framework. I’ve really enjoyed learning about this framework and concept and I think I have a good understanding of how it works but wanted to check with some other people here if my understanding is correct.

My code:

TestRunner.java

package com.example.demoaxon;

import java.util.UUID;

import org.axonframework.commandhandling.gateway.CommandGateway;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class TestRunner implements CommandLineRunner {
	
	private final CommandGateway commandGateway;
	
	@Autowired
	public TestRunner(CommandGateway commandGateway) {
		this.commandGateway = commandGateway;
	}
	
	@Override
	public void run(String... args) throws Exception {
		log.info("send command");
		String id = UUID.randomUUID().toString();
		commandGateway.sendAndWait(new IssueCardCommand(id,100));
		commandGateway.sendAndWait(new RedeemCardCommand(id,90));
		
	}
}

GiftCard.java

package com.example.demoaxon;

import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.eventsourcing.EventSourcingHandler;
import org.axonframework.modelling.command.AggregateIdentifier;
import static org.axonframework.modelling.command.AggregateLifecycle.apply;
import org.axonframework.spring.stereotype.Aggregate;

import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@NoArgsConstructor
@Aggregate
@Slf4j
public class GiftCard {
	
	@AggregateIdentifier
	private String giftCardId;
	private Integer amount;
	
	@CommandHandler
	public GiftCard(IssueCardCommand cmd) {
		log.info("handling {}",cmd);
		apply(new CardIssuedEvent(cmd.getCardId(),cmd.getAmount()));
	}
	
	@EventSourcingHandler
	public void onCardIssuedEvent(CardIssuedEvent evt) {
		log.info("applying {}",evt);
		this.giftCardId = evt.getCardId();
		this.amount = evt.getAmount();
	}
	
	@CommandHandler
	public void redeemCardCommandHandler(RedeemCardCommand cmd) {
		log.info("handling {}",cmd);
		this.amount -= cmd.getAmount();
		apply(new CardRedeemedEvent(cmd.getCardId(),cmd.getTransactionId(),this.amount));
	}
	
	@EventSourcingHandler
	public void onCardRedeemedEvent(CardRedeemedEvent evt) {
		log.info("applying {}",evt);
		this.amount = evt.getAmount();
	}	
}

From what I can understand:

  1. In my TestRunner class, the Command Gateway dispatches the issueCardCommand to it’s CommandHandler using the command bus, which is then creating a new instance of the GiftCard aggregate. In this CommandHandler we can perform any logic, and then we use this apply method.

  2. The apply(event) method is used to publish the CardIssuedEvent as an EventMessage within the scope of the GiftCard aggregate and it also invokes the EventSourcingHandler for that particular event, so in this case onCardIssuedEvent. It publishes the EventMessage to the EventBus and is sent to EventHandlers.

  3. In the @EventSourcingHandler onCardIssuedEvent, we can make any state changes to the GiftCard aggregate and we are also persisting the event to the DOMAIN_EVENT_ENTRY table using spring Jpa.

  4. Once this CommandHandler is finished executing, the aggregate object doesn’t exist anymore.

  5. Now in my TestRunner class again, the Command Gateway dispatches the RedeemCardCommand to its CommandHandler and as the first command no longer exists, the empty no args constructor is used to create the objects. The axon framework retrieves all events from this DOMAIN_EVENT_ENTRY table and it replays all the events (EventSourcingHandlers) for the GiftCard aggregate instance in order to get it’s current state (which is why the @AggregateIdentifier is important).

  6. The RedeemCardCommandHandler method is then executed, it performs any logic and applies the event, which is published within the aggregate and it invokes it’s EventSourcingHandler. This EventSourcingHandler then updates the state of the GiftCard aggregate / persists to the DOMAIN_EVENT_ENTRYtable.

Is my understanding of how the event sourcing works correct?

Thanks, I was able to get my question answered by Lucas on stackoverflow (https://stackoverflow.com/questions/65613627/understanding-of-axon-event-sourcing).

1 Like

Hi @amasukakarot, thanks for letting everyone knows it was already answered!
I believe you can mark your own response as the solution to the thread here :slight_smile:

1 Like