Issue creating an aggregate from a command on another aggregate

Hi,

We’re having an issue when using an aggregate as the factory for a different aggregate. Below a simplified example of what we would like to do:
We have two separate aggregates: Analysis and Function. The analysis should has a method defineFunction which should create a Function after some validation of the Analyis’ state.

@Aggregate
public class Analysis {
@AggregateIdentifier
private UUID id;
private String title;

private Analysis() {}

public Analysis(String title) {
apply(new AnalysisStartedEvent(UUID.randomUUID(), title))
}

public Function defineFunction(String description) {
return new Function(id, description);
}

@EventHandler
public void mutate(AnalysisStartedEvent event) {
id = event.getId();
title = event.getTitle();
}
}

@Aggregate
public class Function {
@AggregateIdentifier
private UUID id;
private UUID analysisId;
private String description;

private Function() {}

public Function(UUID analysisId, String description) {
apply(new FunctionDefinedEvent(analysisId, UUID.randomUUID(), description));
}

@EventHandler
public void mutate(FunctionDefinedEvent event) {
id = event.getId();
analysisId = event.getAnalyisId();
description = event.getDescription();
}
}

public class AnalysisCommandHandler {
private Repository analysisRepository;
private Repository functionRepository;

@Autowired
public AnalysisCommandHandler(Repository analysisRepository, Repository functionRepository) {
this.analysisRepository = analysisRepository;
this.functionRepository = functionRepository;
}

@CommandHandler
public void handle(DefineFunctionCommand command) {
Aggregate analysisAggregate = analysisRepository.load(command.getAnalyisId().toString());
functionRepository.newInstance(() -> analysisAggregate.invoke(analysis -> analysis.defineFunction(command.getDescription())));
}
}

Running the above code results in the following NullPointer exception:

java.lang.NullPointerException: null
at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936) ~[na:1.8.0_112]
at org.axonframework.common.lock.PessimisticLockFactory.lockFor(PessimisticLockFactory.java:100) ~[axon-core-3.0.2.jar:3.0.2]
at org.axonframework.common.lock.PessimisticLockFactory.obtainLock(PessimisticLockFactory.java:90) ~[axon-core-3.0.2.jar:3.0.2]
at org.axonframework.commandhandling.model.LockingRepository.doCreateNew(LockingRepository.java:104) ~[axon-core-3.0.2.jar:3.0.2]
at org.axonframework.commandhandling.model.LockingRepository.doCreateNew(LockingRepository.java:48) ~[axon-core-3.0.2.jar:3.0.2]
at org.axonframework.commandhandling.model.AbstractRepository.newInstance(AbstractRepository.java:79) ~[axon-core-3.0.2.jar:3.0.2]

Which seems to stem from the id of the Function being null. Setting the id in the Function constructor gets rid of the NullPointer but still results in the repository not containing an aggregate with said id.
I came across this discussion https://groups.google.com/d/msg/axonframework/e2dpXxT0jeE/_-VUAVGVEgAJ which describes a similar scenario, which, judging from the discussion, does seem to work in Axon 2. Allard Buijze mentions this would be taken into account for Axon 3, so I’m wondering if there’s something we’ve missed. I can’t seem to find any documentation or examples describing this usecase.

I was able to get the scenario to work by injecting the EventBus in the domain model and publishing the event as a GenericDomainEventMessage with the FunctionId as aggregateidentifier and a sequence of 0. However, we would like to keep all this infrastructure code out of the domain models.

I hope I’ve described our use case thoroughly enough and that someone can provide a solution. If more information is needed, please let me know and I’ll try to provide them.

Thanks in advance,
Jan-Willem

Hi Jan-Willem,

creating Aggregates from another is a pattern that is currently not supported in Axon. We are considering adding this to the next release.

For now, you would need to use the constructor on the Function class in order to create new instances.

Cheers,

Allard

Hi Allard,

are there updates regarding this topic? :slight_smile:

I actually do have a similar problem where a ‘Project’ acts as a factory and should receive different Commands that can lead to the creation of other aggregates.
If its not possible, are there already known and ‘considered good’ workarounds that i did not spot?

Otherwise i would prolly try to make it work like done in the original post or here https://groups.google.com/forum/#!msg/axonframework/e2dpXxT0jeE/_-VUAVGVEgAJ by using an external command handler.

Thanks in advance!

Hi 68616e6e61 (I assume you have another name as well, but hey),

I can tell you that it’s a work in progress now, only needing a more thorough review from Allard’s side.

This issue describes the feature to spawn aggregates from other aggregates.

And this pull request by our colleague Milan will introduce the feature.

It’s slated for release 3.3, so please stay tuned for that!

Cheers,
Steven

Hi Steven,

awesome! Thanks for the info :slight_smile:

Regrards,
68616e6e61 :wink: