No repository provider when spawning a new aggregate

I have two aggregates :

  • User that maintains information of a user (names, email…)
  • Rights : that maintains authorizations granted to an user (its has the user identifier and a list of roles)

When a new user is created in the system, I want that its Rights aggregate be also created (with an empty roles list).

For that I want to use the convenient method AggregateLifecycle.createNew that creates a new aggregate from the current aggregate.

I have this code for the constructor of my User aggregate :

`
@CommandHandler
public UserAggregate(CreateUserCommand command) throws Exception {
String userId = command.getUserId();
AggregateLifecycle.apply(new UserCreatedEvent(userId, …));
// We also create aggregate for rights of this user
OpenUserRightsCommand rightsCommand = new OpenUserRightsCommand(userId);
AggregateLifecycle.createNew(RightsAggregate.class, () -> new RightsAggregate(rightsCommand));
}

`

And the corresponding constructor for the RightsAggregate :

@CommandHandler public RightsAggregate(OpenUserRightsCommand command) { AggregateLifecycle.apply(new UserRightsOpenedEvent(command.getUserId())); }

That is very nice.

But… That fails at execution time with the above error message :

`
WARN org.axonframework.commandhandling.gateway.DefaultCommandGateway - Command ‘xxx.CreateUserCommand’ resulted in org.axonframework.common.AxonConfigurationException(Since repository provider is not provided, we cannot spawn a new aggregate for xxx.RightsAggregate)

`

Why do I need a RepositoryProvider as all my aggregates are configured with their repository. Axon already know the repository of each aggregate class.
My configuration is made manually, as we do not have Sprint in our project :

`
private AggregateConfiguration aggregate(Class aggregateType) {
return AggregateConfigurer
.defaultConfiguration(aggregateType)
.configureRepository(c -> repository(aggregateType, c.eventStore()));
}

private Repository repository(Class aggregateType, EventStore eventStore) {
int snapshotTriggerCount = properties.getEventSnapshotTriggerCount();
Snapshotter snapshotter = new AggregateSnapshotter(eventStore, new GenericAggregateFactory<>(aggregateType));
SnapshotTriggerDefinition snapshotTriggerDefinition = new EventCountSnapshotTriggerDefinition(snapshotter, snapshotTriggerCount);

return new EventSourcingRepository<>(
aggregateType,
eventStore,
snapshotTriggerDefinition);
}

`

Hi Frédéric,

A Repository will be created for every Aggregate instance in your system.
Thus, if you have a UserAggregate and RightsAggregate, both will have a dedicated Repository, in your scenario a EventSourcingRepository and EventSourcingRepository.

These repositories are in charge of hydrating the Aggregate from their given Event Stream, but also to provide the Aggregate with the means to publish events (through the EventStore) and to create new Aggregate instances.
To create a new Aggregate instance, you’d again need the Repository we’ve been talking about.
Thus, the Repository of the Aggregate which wants to be able to instantiate another Aggregate, should have access to that other Aggregate’s Repository.

From your configuration I see that you’re still on Axon 3, as you’re using the constructors i.o. the builders (if possible, I’d recommend to upgrade to 4 to be able to leverage the new features we’ll be adding).

You can simply add a RepositoryProvider (a Functional Interface) component to the Aggregate Repository builder function which should be able to instantiate other Aggregates as well.

To circle back to your question regarding this topic:

Why do I need a RepositoryProvider as all my aggregates are configured with their repository. Axon already know the repository of each aggregate class.

Yes, Axon definitely knows the Repository for your Aggregates.

This information is contained in the Configuration class, which I assume you’re using to configure all your components.

However, as you’ve chosen to instantiate your Repositories manually, the Configuration is not able to inject this RepositoryProvider in your Repository instances.
I’d suggest to introduce another private <T> Repository<T> repository() function in your code, one which has another parameter in it to provide this RepositoryProvider.

You could also follow what the framework does in this scenario, which is the Configuration#repository(Class) function, as the RepositoryProvider.

This function will look up the Repository for a given Aggregate instance, as needed when you’d want to instantiate Aggregates from another Aggregate.

Hope this gives you the needed background Frédéric!

Cheers,
Steven