Aggregate factories in Axon 3

First, the actual question: Is there an example of how to use aggregate factories with an aggregate class hierarchy in Axon 3?

Now the details. Context is that I have a few related aggregate root classes with some of the command handlers defined in the superclass (RequestAggregate) and others in the subclasses (RegisterRequestAggregate, etc.)

In Axon 2, I registered a single factory, attached it to a repository, and registered a command handler for each subclass, like so:

<bean id="requestAggregateFactory" class="com.xyz.RequestAggregateFactory" />

<axon:event-sourcing-repository id="requestAggregateRepository"
                                   aggregate-factory="requestAggregateFactory"
                                   event-bus="eventBus"
                                   event-store="eventStore" />

<axon:aggregate-command-handler aggregate-type="com.xyz.CancelRequestAggregate"
                                   repository="requestAggregateRepository"
                                   command-bus="commandBus" />
<axon:aggregate-command-handler aggregate-type="com.xyz.RegisterRequestAggregate"
                                   repository="requestAggregateRepository"
                                   command-bus="commandBus" />
<axon:aggregate-command-handler aggregate-type="com.xyz.ValidateRequestAggregate"
                                   repository="requestAggregateRepository"
                                   command-bus="commandBus" />

In Axon 3, I try to configure the factory and the aggregates (I'm letting autoconfiguration build the repository for me):

     configurer.configureAggregate(
         AggregateConfigurer.defaultConfiguration(RequestAggregate.class)
             .configureAggregateFactory(c -> new RequestAggregateFactory()));
     configurer.configureAggregate(CancelRequestAggregate.class);
     configurer.configureAggregate(RegisterRequestAggregate.class);
     configurer.configureAggregate(ValidateRequestAggregate.class);

And this sort of works in that the factory gets called to instantiate the aggregate when the initial creation command is dispatched. But none of the subclass command handler methods are recognized; if I try to send a subclass-specific command, Axon throws an exception saying it can't find the command handler. Commands whose handlers are on the superclass work fine.

The problem seems to be that when the configuration is initialized, the AbstractRepository constructor calls ModelInspector.inspectAggregate() and passes it the superclass (as returned by the getType() method on the factory bean) rather than one of the subclasses. So the aggregate model only contains the superclass command handler methods.

It doesn't work to modify the factory to return a subclass from getType() because then the factory will claim it's a producer of, say, CancelRequestAggregate instances but will return a RegisterRequestAggregate instead if you pass it a particular kind of command.

I tried declaring the factory bean with a bunch of aliases so it would be associated with all the aggregates by autoconfiguration, but that ended up behaving the same as the explicit call above.

I also tried getting rid of the factory and seeing if autoconfiguration would figure it out, but that ends up not working either; I get an IncompatibleAggregateException because commands end up getting routed to the wrong subclasses.

Hopefully there's an example I can look at to see how this is supposed to be done; I expect I'm failing to understand something fundamental. I only found one Axon JUnit test that uses aggregate factories and it doesn't use a class hierarchy, just a single aggregate class.

-Steve

Hi Steven,

at the moment, polymorphism isn’t supported to the extent that you can define @CommandHandlers on specific implementations only. It’s still on our wishlist and we have a few ideas on how to implement this, but it’s not a trivial problem to solve.

For now, the only supported way is to define the commands on the (abstract) super class of the aggregate and put the annotations on those methods. The subclasses can override the methods to provide the implementation-specific options.

Alternatively, use ‘external’ CommandHandlers for the implementation specific commands: in a “regular” bean, load the aggregate from the repository, do an instanceof check and invoke a method after casting the aggregate. In Axon 3, you don’t get the instance itself from the Repository, but a wrapper. Use aggregate.execute(ar -> …), or aggregate.invoke(ar ->…) to execute logic on the aggregate root. This (also) ensures that apply() applies events to the correct aggregate instance.

The AggregateFactory is responsible for instantiating the correct instance of an aggregate, which it needs to do based on the first event. The ensures that the implementation returned from the repository

Hope this helps.
Cheers,

Allard

This topic has been lingering for a while. It was never forgotten, but just part of a backlog that had some, let’s say, higher priority issues on it. But it’s time to address it.

The problem I’m primarily facing is the API design for this. Both using Spring Boot autoconfiguration and the AggregateConfigurer, it should be fairly easy (technically) to manage polymorphism. The main thing Axon needs to do, is find all possible implementations for an aggregate class, and register them. Preferably, this would also include the (type of) event that defines an aggregate is of that specific type. In that case, Axon will automatically know which instance to create when sourcing it from events.

Basically, I see globally two options:

  1. Define the possible implementations (and their creation event type) in the abstract parent class. On one hand, this creates coupling of a superclass to its implementing classes (which is not ‘pure’ in OO terms), but on the other hand, it creates a good overview.

Example:

@Aggregate(implementations = {@PolymorphicAggregate(eventType = SomeImplementationCreatedEvent.class, aggregateType = SomeImplementation.class})
public abstract class SomeAggregateSuperClass {

}

  1. Each subclass declares itself as a specialized implementation.

@Aggregate
public abstract class SomeAggregateSuperClass {

}

@PolymorphicAggregate(initialEvent = SomeImplementationCreatedEvent.class)
public class SomeImplementation extends SomeAggregateSuperclass {
}

The super class doesn’t need to be abstract. Basically, that type would be created if the creation event is anything else than the ones specified on the subclasses. Also, the initial event isn’t necessary if the Aggregate isn’t event sourced. In that case, it should have proper JPA annotations to indicate how implementations are stored and retrieved.

I’m hoping to be able to include this in 3.2, although we’re on the brink of releasing that version.
Any feedback is welcome.

Cheers,

Allard