GoldFlsh
GoldFlsh

Reputation: 1334

EventSourceHandler being triggered by the wrong event?

I am experiencing an issue where an event is being triggered that I would imagine shouldn't be. I am playing with axon and am modeling a letter sending service based on letter sending events. My example is kind of trivial.

I have designed two input commands ScheduleLetterCommand which will "send a letter" in a few minutes...which allows the user to cancel it if they send another command CancelLetterCommand within that time period.

I am triggering commands via a REST API Controller...

What I am expecting to see is the log statements that say a letter is scheduled and cancelled if I were to schedule a letter and immediately cancel it within the next five minutes.

What I am curiously seeing is that the letter is scheduled successfully and I see the following log statements:

Received schedule command for letter id e6e037be-3b6d-4ae3-80cd-12426adcd526
LetterScheduledEvent e6e037be-3b6d-4ae3-80cd-12426adcd526 SCHEDULED

But when I cancel the letter I see this behavior:

LetterScheduledEvent e6e037be-3b6d-4ae3-80cd-12426adcd526 SCHEDULED
Received cancel command for letter id e6e037be-3b6d-4ae3-80cd-12426adcd526
Letter e6e037be-3b6d-4ae3-80cd-12426adcd526 cancelled CANCELLED

What am I missing here? Why is the LetterScheduledEvent handler being triggered again?

Here's my aggregate -

public class Letter {

    @AggregateIdentifier
    private String letterId;
    private ScheduleToken scheduleToken;

    @SuppressWarnings("UnusedDeclaration")
    protected Letter() {
        // Default constructor required by Axon Framework
    }

    @CommandHandler
    public Letter(ScheduleLetterCommand cmd, EventScheduler scheduler) {
        String id = cmd.getLetterId();
        log.info("Received schedule command for letter id {}", letterId);
        AggregateLifecycle.apply(new LetterScheduledEvent(id, LetterEventType.SCHEDULED));
        this.scheduleToken = scheduler.schedule(Duration.ofMinutes(5), new BeginSendLetterEvent(id,LetterEventType.BEGIN_SEND));
    }

    @CommandHandler
    public void handle(CancelLetterCommand cmd, EventScheduler eventScheduler) {
        String letterId = cmd.getLetterId();
        log.info("Received cancel command for letter id {}", letterId);
        AggregateLifecycle.apply(new LetterCancelledEvent(letterId, LetterEventType.CANCELLED));
        eventScheduler.cancelSchedule(scheduleToken);
    }

    @EventSourcingHandler
    public void on(LetterScheduledEvent event) {
        log.info("LetterScheduledEvent {} {}", event.getLetterId(), event.getEventType());
        this.letterId = event.getLetterId();
    }

    @EventSourcingHandler
    public void on(LetterCancelledEvent event) {
        log.info("Letter {} cancelled {}", event.getLetterId(), event.getEventType());
        scheduleToken = null;
    }

    @EventSourcingHandler
    public void on(BeginSendLetterEvent event) {
        log.info("Letter sending process started {} {}", event.getLetterId(), event.getEventType());
        //complicated letter sending processes...
        AggregateLifecycle.apply(new LetterSentEvent(event.getLetterId(), LetterEventType.SENT));
    }

    @EventSourcingHandler
    public void on(LetterSentEvent event) {
        log.info("Letter sent {} {}", event.getLetterId(), event.getEventType());
    }
}

And here's my events ->

abstract class LetterMovementEvent(open val letterId: String, open val eventType: LetterEventType)

enum class LetterEventType {
    SCHEDULED,
    CANCELLED,
    BEGIN_SEND,
    SENT
}

data class LetterScheduledEvent(
        override val letterId: String,
        override val eventType: LetterEventType = LetterEventType.SCHEDULED
) : LetterMovementEvent(letterId, eventType)

data class LetterCancelledEvent(
        override val letterId: String,
        override val eventType: LetterEventType = LetterEventType.CANCELLED
) : LetterMovementEvent(letterId, eventType)

data class BeginSendLetterEvent(
        override val letterId: String,
        override val eventType: LetterEventType = LetterEventType.BEGIN_SEND
) : LetterMovementEvent(letterId, eventType)

data class LetterSentEvent(
        override val letterId: String,
        override val eventType: LetterEventType = LetterEventType.SENT
) : LetterMovementEvent(letterId, eventType)

Upvotes: 1

Views: 107

Answers (1)

Steven
Steven

Reputation: 7275

What you're noticing @GoldFish, is the framework which is sourcing your aggregate based on the events it has published. So, shortly put, you are seeing "Event Sourcing in action".

With the @EventSourcingHandler annotation in the Letter aggregate, you have effectively created the methods used to re-create an aggregate based on events.

Thus, if you send a command to cancel this aggregate's letter, it will firstly recreate the aggregate from the events. Only once this is resolved will the command actually be given to the @CommandHandler annotated method.

Hope this clarifies it for you @GoldFish!

Upvotes: 3

Related Questions