Aggregate Polymorphism

In certain cases it is beneficial to have a polymorphic hierarchy in aggregate structure. Subtypes in polymorphic aggregate hierarchy inherit @CommandHandler`s, `@EventSourcingHandler`s and `@CommandHandlerInterceptor`s from the super aggregates. Based on `@AggregateIdentifier the correct aggregate type is loaded and command is executed on it. Let’s take a look at the following example:

public abstract class Card {}

public class GiftCard extends Card {}

public class ClosedLoopGiftCard extends GiftCard {}

public class OpenLoopGiftCard extends GiftCard {}

public class RechargeableGiftCard extends ClosedLoopGiftCard {}

We can define this structure as Polymorphic Aggregate of type GiftCard and subtypes of ClosedLoopGiftCard, OpenLoopGiftCard, and RechargeableGiftCard. If there are handlers present on Card class, those will be present on all aggregates as well.

While modeling a polymorphic aggregate hierarchy it is important to keep these constraints in mind:

  • It is not allowed to have a constructor annotated with @CommandHandler on abstract aggregate. The rationale for this is that an abstract aggregate can never be created.

  • Having creational command handlers of the same command name on different aggregates in the same hierarchy is forbidden too, since Axon cannot derive which one to invoke.

  • In a polymorphic aggregate hierarchy it is not allowed to have multiple @AggregateIdentifier and @AggregateVersion annotated fields.

Registering aggregate subtypes

A polymorphic aggregate hierarchy can be registered via the EventSourcingConfigurer.

public class AxonConfig {
    // omitting other configuration methods...
    public void configure(EventSourcingConfigurer configurer) {
        configurer.registerEntity(
            EventSourcedEntityModule.declarative(String.class, GiftCard.class)
                                    .messagingModel(PolymorphicEntityMetamodel.forSuperType(GiftCard.class)
                                                                              .addConcreteType(AnnotatedEntityMetamodel.forType(OpenLoopGiftCard.class))
                                                                              .addConcreteType(AnnotatedEntityMetamodel.forType(RechargeableGiftCard.class))
                                                                              .build())
                                    // other phases (factory, etc.) omitted for brevity
        );
    }
}

class GiftCard {
    // omitted implementation for brevity
}

class OpenLoopGiftCard extends GiftCard {
    // omitted implementation for brevity
}

class RechargeableGiftCard extends GiftCard {
    // omitted implementation for brevity
}

Polymorphic aggregates in spring boot

If you are using Spring, Axon will automatically detect polymorphic aggregates based on the @EventSourced annotations and class hierarchy. The @EventSourced annotation needs to be put on the shared parent class that contains the aggregate identifier, as well as every subclass that is a potential instance type of that shared parent class.

@EventSourced(concreteTypes = {OpenLoopGiftCard.class, RechargeableGiftCard.class})
public abstract class GiftCard {

    @EntityId
    protected String cardId;

    // ...
}

@EventSourced
public class OpenLoopGiftCard extends GiftCard {
    // ...
}

@EventSourced
public class RechargeableGiftCard extends GiftCard {
    // ...
}