Multi commandhandle for a command

Dear all !

I using Axon 3.0.6 for my project. I have 2 tables : User and Department (Department one-to-many with User) . When I want delete a record in User table, I raise a command (DeleteDepartmentCommand) for DeleteDepartmentCommandHandler to delete it.
To delete i want check in User table, I create new ValidateUserWhenDeleteDepartmentCommandHandle with param is DeleteDepartmentCommand, but it not running. Anyone can help me : How to config (or design) to multi commandhandle for a command.

Sorry about my English :frowning:

Thanks so much !

Hi,

commands always invoke only a single component. You cannot have multiple handlers for the same command (at least, all but one get ignored).
It sounds like you’re doing a query: “are there any users left in this department?”. If that’s the case, you’d be better off modelling it is a query.

Cheers,

Allard

Dear Allard,

Thanks for your supported.
In our design architecture, we intent to decouple User and Department into separated modules, which User Module (and others) depends on Department Module. So:

  • Department has no idea of the existence of User Module
  • Once the Department is going to be deleted, other dependent modules (User, etc.) need to reject the deletation if they depends on the deleting Department
  • User Module is easily removed without touching Department Module source code

In the case above:

  • There is a DeleteDepartmentCommandHandler (stays inside Department Module), to process deleation of the requested department
  • There is a command handler in User Module, try to handle DeleteDepartmentCommand, check usage of the deleting department, and try to reject.

Do you have any idea of how to implement this?

Thank you so much!

Hello,
I’m still relatively new to Axon/CQRS+ES, but I’ll take a stab at it as it usually takes about 3 days to get a response from Allard (your timing must have been just right to get a response in just 4 hours). Typically, you’re going to have Command -> Event(s). Since department is supposed to be oblivious to the users, it seems like your RemoveDepartment command would result in a RemoveDepartmentRequested event that the User module would listen for. In that listener is where you would perform the “Does this department have any users?” query to then determine whether or not to actually delete the Department. Something I’m unclear on is whether you could just raise an event saying RemoveDepartmentApproved or if you have to issue a follow-up command ApproveRemoveDepartment which would then result in a DepartmentRemoved event. Not sure if Sagas would come into play here or not (perhaps if the requirements for allowing it to happen in the first place become more complex), but based on my current understanding this is how I imagine it would work. So, if I’m wrong in my thinking on this, please let me know for my own benefit.

There are several ways of approaching this. First, the choice of aggregate boundaries must be done with thought. The fact that there are two nouns involved, doesn’t mean there are also 2 aggregates. It may be that User is an entity in the Department Aggregate.

If they really are 2 separate aggregates, you have, again, a few options.

  1. Use the reservation pattern. This is essentially when Brian describes. First action describes an intent. You then do other actions and confirm the initial intent. This is quite cumbersome, but may give you strong consistency.
  2. Do a query to validate the intent before deleting the Department. Simply query for any remaining users. If there aren’t any, allow the deletion of the Department, otherwise block it.
  3. When creating a User, record the identifier with the Department. When deleting a User, remove it from there. When a department is deleted (which is only possible when all users were properly unregistered), User registration will fail and you will be able to compensate by disabling the newly created user account.

Item number 2 may seem “scary” because of the eventual consistency here, but don’t fool yourself. How big is the chance of something like this happening? If it happens, what’s the damage? My point here: don’t waste precious hours on solving an issue that hardly ever occurs and doesn’t cost much to fix when it does.

Cheers,

Allard