Issue creating an aggregate from a command on another aggregate

632 views
Skip to first unread message

Jan-Willem Willebrands

unread,
Apr 13, 2017, 4:04:52 AM4/13/17
to Axon Framework Users
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<Analysis> analysisRepository;
private Repository<Function> functionRepository;
@Autowired
public AnalysisCommandHandler(Repository<Analysis> analysisRepository, Repository<Function> functionRepository) {
this.analysisRepository = analysisRepository;
this.functionRepository = functionRepository;
}
@CommandHandler
public void handle(DefineFunctionCommand command) {
Aggregate<Analyis> 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

Allard Buijze

unread,
Apr 14, 2017, 10:20:47 AM4/14/17
to Axon Framework Users
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

--
You received this message because you are subscribed to the Google Groups "Axon Framework Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to axonframewor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

68616...@gmail.com

unread,
May 18, 2018, 3:46:23 AM5/18/18
to Axon Framework Users
Hi Allard,

are there updates regarding this topic? :)

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!

Steven van Beelen

unread,
May 18, 2018, 5:29:43 AM5/18/18
to axonfr...@googlegroups.com
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

68616...@gmail.com

unread,
May 18, 2018, 7:07:21 AM5/18/18
to Axon Framework Users
Hi Steven,

awesome! Thanks for the info :)

Regrards,
68616e6e61 ;)
Reply all
Reply to author
Forward
0 new messages