Advanced Use Cases

The current example, doesn’t use the ability to cancel deadlines. In the case of a gift card, it might be a business rule to publish an event that the gift card expired if it was already depleted. Let’s make a few changes to enable this. The first thing to need to do is to add a remainingValue to the aggregate, so it’s known when there is no value remaining. Then, adding to the command handler:

@CommandHandler
public void handle(RedeemCardCommand command, DeadlineManager deadlineManager) {
    //check validity
    apply(new CardRedeemedEvent(giftCardId, command.amount()));
    if (remainingValue == 0) {
        deadlineManager.cancelAllWithinScope(EXPIRED_GIFT_CARD); (1)
    }
}
1 This call cancels all deadlines with the name EXPIRED_GIFT_CARD for this aggregate instance. It’s possible to use the return value from scheduling a deadline to cancel a specific deadline. In this case, there can be at most one deadline scheduled with this name, so this is easier.

And the event sourcing handler:

@EventSourcingHandler
public void on(CardRedeemedEvent event) {
    remainingValue -= event.amount();
}

A test to make sure the deadline cancellation was successful:

@Test
void testCardNotExpiringIfNothingLeft() {
    testFixture.givenNoPriorActivity()
               .andGivenCommands(
                       new IssueExpiringCardCommand(CARD_ID, DAYS, AMOUNT),
                       new RedeemCardCommand(CARD_ID, AMOUNT)
               )
               .whenTimeElapses(Duration.ofDays(31L))
               .expectSuccessfulHandlerExecution()
               .expectNoEvents();
}

This should help get you started with deadlines. Don’t forget to switch the implementation to one of the deadline managers which offer persistence before moving to prod.