Using an aggregate to validate a command which creates a new aggregate of the same type

Hi all. I’m new to this framework, so apologies if I’m missing something really basic.

This is for a new open source project, the full code exhibiting this issue can be found here:
https://github.com/ddouglascarr/liquidcanon-api/tree/unit-cqrs

The rule I’m trying to implement is: “a member can create a new member only if they are an admin”. My problem is that I’m getting an EventStoreException in a test.

Both admin and non-admin members use the same aggregate, the only difference is that admins have an admin attribute. (accessed from MemberAggregate.getAdmin())

I have written different commands and events for creating admins and non-admins, but they both operate on the same aggreagate (MemberAggregate).

The above rule is implemented in this command handler (https://github.com/ddouglascarr/liquidcanon-api/blob/unit-cqrs/src/main/java/org/ddouglascarr/command/member/MemberCommandHandler.java):

@CommandHandler
public void handleCreateMember(CreateMemberCommand command)
        throws MemberUnprivilegedException
{
    MemberAggregate requester = repository.load(command.getRequestingMemberId());
    if (!requester.getAdmin()) {
        throw new MemberUnprivilegedException();
    }
    MemberAggregate memberAggregate = new MemberAggregate(
            command.getId(), command.getLogin(), command.getPassword(),
            command.getNotifyEmail(), false );
    repository.add(memberAggregate);
}

This test is failing (https://github.com/ddouglascarr/liquidcanon-api/blob/unit-cqrs/src/test/java/org/ddouglascarr/integration/command/MemberCommandComponentTests.java):

@Test
public void createMemberShouldCreateMember() throws Exception
{
    fixture.given(new AdminMemberCreatedEvent(ADMIN_MEMBER_ID, ADMIN_LOGIN, PASSWORD))
            .when(new CreateMemberCommand(ADMIN_MEMBER_ID, MEMBER_ID, LOGIN, PASSWORD, EMAIL))
            .expectEvents(new MemberCreatedEvent(MEMBER_ID, LOGIN, PASSWORD, EMAIL));
}

It fails with:

org.axonframework.eventstore.EventStoreException: Writing events for an unexpected aggregate. This could indicate that a wrong aggregate is being triggered.

If I remove the load command and validation logic from the command handler, the test fails with:

java.lang.AssertionError: The aggregate used in this fixture was initialized with an identifier different than the one used to load it. Loaded [a4a031fd-57a4-4666-b1fd-afcb75811111], but actual identifier is [a4a031fd-57a4-4666-b1fd-afcb75800000].
Make sure the identifier passed in the Command matches that of the given Events.

The UUID in the message equal MEMBER_ID and ADMIN_MEMBER_ID respectively

What am I doing wrong here?
Is it not possible to load an aggregate of the same type within a command handler?
If so, should I be using the query system to validate the rule, not the command system?

Thanks for your help,
Daniel

Hi Daniel,

The reason why your test fails is because Axon’s text fixtures are designed to work with a single aggregate instance but in your case you have 2 aggregates (the admin member’s aggregate and the new member’s aggregate).

There’s a couple ways to go about this but by far the easiest (and best) way to go is to leave user role validation completely outside of the aggregate.

If a request comes in to create a new member the application should already know whether the request comes from an admin or a regular user (based on security cookies). Just as you would in a traditional application it will only carry out the request if the user is an admin.

If the signed user has the required rights you simply dispatch the command to create a new member. This command will not bother the admin member aggregate.

However, if it is essential that you do go through the admin aggregate for whatever reason (though probably not a valid reason ;)) then you should have the admin member aggregate publish an event that a new member has been allowed access to the platform. An event handler can then issue a command to create the new member in response to this event.

Finally, I really recommend using @CommandHandler inside of the aggregate instance as opposed to having a dedicated command handler that loads the aggregate from a repository to carry out the command. Aside from removing boilerplate code it also lets the aggregate be more independent as it does not need to expose any of its methods or internals to outside components.

Regards,
Rene

Thanks for your reply.

I will do what you recommend with the CommandHandler. I missed the section of the docs on how to implement that within the aggregate, and that seems a lot cleaner and easy to reason about. Thanks.

With regards to validation, I would like some guidance on best practices. I’ve been thinking that the job of a command handler is to validate a command, then, dispatch an event or throw an error. I’ve been thinking of “command” as equivalent to “request”. It is a representation of the user’s wishes. The application does not assume that they are reasonable or allowed. Your comment suggests I’ve got this wrong.

For example, consider a blog application where users can post comments on a blog entry. A constructor signature for a command to post a comment might look like:

CreateBlogPostCommentCommand(UUID commentId, UUID commentingUserId, UUID blogPostId, String commentText)

The rules that would need to be checked before issuing an event could be:

  • blogPostId refers to an existing blog post
  • That blog post has comments enabled
  • commentingUserId references an existing, enabled user
  • That user has not been blocked from commenting on blog posts by the owner of the blogPostId blog post
  • The commentText does not contain any banned words

I have 2 main questions about such a scenario

  • Is the command handler the place to do this validation? (If not, would best practice be a pre-command service layer? Is a Command Interceptor what I’m looking for? )
  • Is it best practice to use the query components to check these things? Is it considered better in any circumstances to use the command components instead? (e.g. to check that the blog post allows comments, do I just use the QueryBlogPostService, or do I use the BlogPostAggregate?)

Thanks again,
Daniel

Hi Daniel,

No, you haven’t got it wrong. A command handler is definitely the correct place to validate a command.

However, your aggregate should only validate the state it finds relevant. Another way of looking at this is that the aggregate is ‘unaware’ of the outside world (because it falls outside of its domain).

So in your example, assuming a blog post is an aggregate then it can indeed check if comments are enabled and if the comment does not contain any foul language. However, the aggregate will not check if the id of the blog post exist. Technically this is already the case, since otherwise the command would not even reach the aggregate, but more fundamentally because the aggregate is not aware that there are other blog posts in the world. Same goes for checking if the user exists. That is not the job of the blog post aggregate and again: the aggregate is not really aware of the concept of user accounts.

So where to put this extra validation? Normally you’d handle that in your service layer or in interceptors, like you mentioned.

Regards,
Rene