Not able to get an example of @AggregateMember to work correctly.

I have taken the AxonBank example (https://github.com/AxonFramework/AxonBank/) - and added a single Map to represent a parent child relationship. This is the addition to BankAccount aggregate.

`

@AggregateMember
private Map<String, Member> members;

@EventSourcingHandler
public void on(MemberAdded evt){
    System.out.println(evt);
}

@CommandHandler
public void on(AddMemberCommand command) {
    System.out.println(command);
}

`

I then have a new class Member

`

public class Member {

    @EntityId
    private String memberId;

    @CommandHandler
    public Member(AddMemberCommand cmd){
        apply(new MemberAdded(cmd.getMemberId()));
    }

    @EventSourcingHandler
    public void on(MemberAdded evt){
        this.memberId = evt.getMemberId();
    }
}

`

I then have a new command.

`

public class AddMemberCommand {
    @TargetAggregateIdentifier
    private String bankAccountId;

    private String memberId;

    public AddMemberCommand(String bankAccountId, String memberId) {
        this.bankAccountId = bankAccountId;
        this.memberId = memberId;
    }

    public String getBankAccountId() {
        return bankAccountId;
    }

    public void setBankAccountId(String bankAccountId) {
        this.bankAccountId = bankAccountId;
    }

    public String getMemberId() {
        return memberId;
    }

    public void setMemberId(String memberId) {
        this.memberId = memberId;
    }
}

`

I can not get this example to work - I get the following stack trace.

`

java.lang.NullPointerException: null
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:57)
at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
at java.lang.reflect.Field.get(Field.java:393)
at org.axonframework.common.ReflectionUtils.getFieldValue(ReflectionUtils.java:66)
at org.axonframework.commandhandling.model.inspection.AggregateMemberAnnotatedChildEntityMapDefinition.resolveCommandTarget(AggregateMemberAnnotatedChildEntityMapDefinition.java:72)
at org.axonframework.commandhandling.model.inspection.AbstractChildEntityDefinition.lambda$createChildDefinition$0(AbstractChildEntityDefinition.java:61)
at org.axonframework.commandhandling.model.inspection.ChildForwardingCommandMessageHandlingMember.handle(ChildForwardingCommandMessageHandlingMember.java:93)

`

What am I doing wrong, and am I approaching the problem incorrectly.

What I want to do is have the aggregate manage the relationship of being the root/parent of the other. Should I abandon this approach and just use Member as an aggregate on its own?

Thanks,
Carter

I take it by the lack of response, means I should not model it this way, but instead create child aggregates that reference the parent.

-c

Hi Carter,

The lack of response is not a form of answer here. Just us being very busy is all. Sorry to keep you waiting!

The NullPointerException most likely originates because the Members Map you create is null.

However, that’s not what I’d like to suggest you do to solve this.

Your command looks fines, but it’s the BackAccount aggregate and the Member entity you’ve introduced which have a minor flaw.

You’ve assumed that the Member entity should also be instantiated by a Command Handling constructor, just like you’d instantiate an Aggregate Root through a command handling constructor.
You should however let the Aggregate Root be in charge of creating it’s entities. More specifically, it should call the (regular, not command handling) constructor of your entity in an Event Sourcing Handler.

So, if you keep the Command Handler for your AddMemberCommand in the BackAccount aggregate and introduce an @EventSourcingHandler annotated function for the MemberAddedEvent in that same aggregate which will instantiate a Member entity and add it to your members map, then you should be done.

Secondly, you’ve got two @CommandHandler annotated functions for your AddMemberCommand.

As a rule of thumb you can only have one Command Handler per command, so keep that in mind for the future too.

Hope this is clear Carter, feel free to ask more questions about this!

Cheers,

Steven

thanks so much for the response - let me work out the example as you have proposed.

I did not mean to offend with my comment, and I can certainly understand being very busy! I do appreciate all the work that has gone into this framework, as it has really helped me to understand and use event sourcing with CQRS in production!

one follow up question is more conceptual - when/why would we want to use AggregateMember vs having a separate aggregate referencing the parent?

Thanks again for all the hard work and the response!

-Carter

Hi Carter,

Cool, hope my suggestion works out for you! :slight_smile:

Glad to hear the framework is helping you out understanding ES and CQRS, always nice to hear that.

Now, to your following question.

The simplest, but not to satisfying answer to this, is ‘it depends’…

Now, that’s not overly useful of course, so I’ll try to elaborate it some what.

I’d typically make the trade off of introducing an Aggregate Member or a new aggregate based on how much work (commands) your Entity will handle.

When handling a command, the entire Aggregate Root will be locked out of handling any other commands until handling has finished for that initial command.

Thus, if your entity, especially if it’s a list/map of entities, will handle much more than it’s parent, that’ll mean the parent will be locked out of handling any other commands whilst the entities are performing work.

If this would be the case, it might be sensible to let that entity be a separate Aggregate i.o. an Aggregate Member.

This will however also have certain drawbacks, for example with the references between your aggregate and those entities which aren’t implicit any more but will require additional work.

Hence my reason to give you the answer ‘it depends’.

That’s my 2 cents.

Cheers,

Steven

Steven,

While ‘it depends’ is not satisfying, I think its appropriate. I understand the tradeoffs better now, and will likely go the route of a separate aggregate, mainly for the complexity/work handling reason. The member I have in mind, while is related to another aggregate as a child, will be doing enough work to make it separate.

Thanks for taking the time to respond.

-Carter

Hi Carter,

I’ve seen that scenario occur more often.
You’ve got an entity which domain wise is a child of your aggregate, but for technical reasons isn’t a good fit as an actual member under that aggregate.

Glad to have been of help and hoping to see more questions from you in the future!

Cheers,

Steven