Polymorphic Aggregate Migration

Polymorphic aggregates allow you to define a hierarchy where subtypes inherit command and event sourcing handlers from their superclasses. For a detailed guide on how to model these, please refer to the entity polymorphism reference.

Let’s take a look at the following example:

public abstract class Card {}

public class GiftCard extends Card {}

public class OpenLoopGiftCard extends GiftCard {}
public class RechargeableGiftCard extends GiftCard {}

In Axon Framework 4, polymorphic aggregates were often configured using Spring’s @Aggregate annotation on both the base and subclasses. When using the manual configuration API, you would use AggregateConfigurer.

In Axon Framework 5, you register the hierarchy through the EventSourcingConfigurer.

  • Axon Framework 5 (Declarative)

  • Axon Framework 5 (AutoDetected)

  • Axon Framework 4

import org.axonframework.eventsourcing.EventSourcedEntityFactory;
import org.axonframework.eventsourcing.configuration.EventSourcedEntityModule;
import org.axonframework.eventsourcing.configuration.EventSourcingConfigurer;
import org.axonframework.messaging.core.MessageTypeResolver;
import org.axonframework.messaging.core.annotation.ParameterResolverFactory;
import org.axonframework.messaging.core.conversion.MessageConverter;
import org.axonframework.messaging.eventhandling.conversion.EventConverter;
import org.axonframework.messaging.eventstreaming.EventCriteria;
import org.axonframework.messaging.eventstreaming.Tag;
import org.axonframework.modelling.entity.PolymorphicEntityMetamodel;
import org.axonframework.modelling.entity.annotation.AnnotatedEntityMetamodel;

public class AxonConfig {
    // ...
    public void configure(EventSourcingConfigurer configurer) {
         configurer.registerEntity(
                EventSourcedEntityModule.declarative(String.class, GiftCard.class)
                        .messagingModel((configuration, builder) -> PolymorphicEntityMetamodel.forSuperType(GiftCard.class)
                                .addConcreteType(AnnotatedEntityMetamodel.forConcreteType(
                                        OpenLoopGiftCard.class,
                                        configuration.getComponent(ParameterResolverFactory.class),
                                        configuration.getComponent(MessageTypeResolver.class),
                                        configuration.getComponent(MessageConverter.class),
                                        configuration.getComponent(EventConverter.class)
                                ))
                                .addConcreteType(AnnotatedEntityMetamodel.forConcreteType(
                                        RechargeableGiftCard.class,
                                        configuration.getComponent(ParameterResolverFactory.class),
                                        configuration.getComponent(MessageTypeResolver.class),
                                        configuration.getComponent(MessageConverter.class),
                                        configuration.getComponent(EventConverter.class)
                                ))
                                .build())
                        .entityFactory(c -> EventSourcedEntityFactory.fromNoArgument(OpenLoopGiftCard::new))
                        .criteriaResolver(c -> (id, ctx) -> EventCriteria.havingTags(Tag.of("cardId", id)))
                        .build()
        );
    }
}

In most cases, you can rely on auto-detection of polymorphic entities by Axon Framework 5. Simply register the base type, and the framework will discover the subtypes automatically.

import org.axonframework.eventsourcing.annotation.EventSourcedEntity;
import org.axonframework.eventsourcing.annotation.reflection.EntityCreator;
import org.axonframework.eventsourcing.annotation.reflection.InjectEntityId;

@EventSourcedEntity(
    tagKey = "cardId",
    concreteTypes = {
            OpenLoopGiftCard.class,
            RechargeableGiftCard.class
    }
)
public class GiftCard extends Card {
    // ...

    @EntityCreator
    protected GiftCard(@InjectEntityId String cardId) {
        // ...
    }
}

import org.axonframework.eventsourcing.configuration.EventSourcedEntityModule;
import org.axonframework.eventsourcing.configuration.EventSourcingConfigurer;

public class AxonConfig {
    // ...
    public void configure(EventSourcingConfigurer configurer) {
        configurer.registerEntity(
                EventSourcedEntityModule.autodetected(String.class, GiftCard.class)
        );
    }
}
public class AxonConfig {
    // ...
    public void configure(Configurer configurer) {
        Set<Class<? extends GiftCard>> subtypes = Set.of(
            OpenLoopGiftCard.class,
            RechargeableGiftCard.class
        );

        configurer.configureAggregate(
            AggregateConfigurer.defaultConfiguration(GiftCard.class)
                               .withSubtypes(subtypes)
        );
    }
}

See also

  • Polymorphic Entities: full reference for @EventSourcedEntity(concreteTypes = {…​}), the @EntityCreator factory pattern, and PolymorphicEntityMetamodel configuration in Axon Framework 5