Implementing Deadlines

The SimpleDeadlineManager is part of the messaging module of Axon Framework and uses Java’s ScheduledExecutorService, therefore, it requires no additional dependencies. You can configure the SimpleDeadlineManager either as an Axon Configuration component or as a Spring Bean. For this how-to, it’s configured as a Spring Bean.

@Bean
public DeadlineManager deadlineManager(
        org.axonframework.config.Configuration configuration
) {
    var provider = new ConfigurationScopeAwareProvider(configuration);
    return SimpleDeadlineManager
            .builder()
            .scopeAwareProvider(provider)
            .build();
}

Depending on the deadline manager there might be multiple values set on the builder. All deadline managers at least need to set the scope-aware provider.

@Aggregate
public class ExpiringGiftCard {

    @AggregateIdentifier
    private String giftCardId;
    private boolean expired;
    // some other state left out for brevity

    private static final String EXPIRED_GIFT_CARD = "EXPIRED_GIFT_CARD";

    @CommandHandler
    @CreationPolicy(AggregateCreationPolicy.CREATE_IF_MISSING)
    public void handle(IssueExpiringCardCommand command, DeadlineManager deadlineManager) { (1)
        //check validity
        apply(new ExpiringCardIssuedEvent(command.id(), command.daysValid(), command.amount()));
        Instant trigger = Instant.now().plus(command.daysValid(), ChronoUnit.DAYS).truncatedTo(ChronoUnit.DAYS);
        deadlineManager.schedule(trigger, EXPIRED_GIFT_CARD); (2)
    }

    @DeadlineHandler(deadlineName = EXPIRED_GIFT_CARD) (3)
    public void on() {
        if (!expired){ (4)
            apply(new ExpiringCardExpiredEvent(giftCardId));
        }
    }

    @EventSourcingHandler
    public void on(ExpiringCardIssuedEvent event) {
        giftCardId = event.id();
        expired = false;
    }

    @EventSourcingHandler (5)
    public void on(ExpiringCardExpiredEvent event) {
        expired = true;
    }

    // More handlers are needed to make it useful, left out for brevity.

    public ExpiringGiftCard() {
        // Required by Axon to construct an empty instance to initiate Event Sourcing.
    }
}
1 Adding the deadline manager to the method makes it easily testable compared to other ways of accessing it from the aggregate. You can inject this way any component registered in the Axon Configuration, or when using Spring, any bean in the Spring Context.
2 Schedule the deadline, so it triggers at midnight.
3 It’s important to use the same in the annotation as when calling the schedule method.
4 An idempotency check, so even if there is some problem with the deadline manager, the application sends only one ExpiringCardExpiredEvent event.
5 On the ExpiringCardExpiredEvent, change the aggregate’s state.