How to send a callback command to a module which initiated a certain process?

Hi,

I have the following situation:
My application contains three modules: ModuleA, ModuleB and Dashboard. (Starts like a joke, hmm? :wink: )

The Dashboard module handles a TaskCreated even which can be emitted by moduleA or moduleB.
The Dashboard itself handles a ClaimTask-Command to claim a task for a certain user. The actual claiming is not performed by the Dashboard module but needs to be delegated to the module which emitted the corresponding TaskCreated event initially.

Actually we don’t know how to achieve this.

A generic ClaimTask command is not possible as Axon needs a single CommandHandler for each command type.

Having specific commands for each module (ClaimTaskInModuleA and ClaimTaskInModuleB) is an option, but the Dashboard shouldn’t be aware of the other modules and shouldn’t know which other modules exist.

We ended up in adding a claimCommandQueryName to the TaskCreated event. The emitting module tells the Dashboard which Query it has to call to obtain the command which can be used to claim the task. moduleA provides a QueryHandler for moduleAClaimCommandQuery and returns a ClaimTaskInModuleA command instance.
Dashboard calls this query by using the event’s claimCommandQueryName, gets back a specific command and sends this command using the CommandGateway in order to invoke the command handler of the module which emitted the TaskCreated event initially.

That’s somehow cumbersome and I wonder if there is a better way to implement this.

Thanks for your ideas!

I’m not sure I fully understand the use case but I personally would try the following

  • have dashboard handle the RequestForClaimingATaskCommand with some task identifier. While handing that command, the dashboard would need to decide if the request is valid and if should be dispatched.
  • have dashboard send ClaimTaskCommand with the respective Task aggregate identifier (and perhaps some extra information about the task). It does not need to know who will handle that command.
  • have Task aggregates in moduleA, moduleB, …, ModuleN that handle the ClaimTaskCommand While doing so each module would have to decide (based on the command payload, aggregate identifier, extra information passed, …) if it should process the command or ignore it. The module that handles the command will publish TaskClaimedEvent indicating state update of a task it is responsible for.
  • have dashboard listen to TaskClaimedEvent to know when given task is been claimed (and by who if needed).

I’m not sure if this is the best way nor if it fits your use case. Just trying to provide a fully decoupled way of handling the issue at hand as I understand it.

That’s the point. It’s not possible to have multiple aggregates handling the same command type, is it?

Oh my bad. I mixed commands and events. Let me try again.

  • have dashboard handle the RequestForClaimingATaskCommand with some task identifier. While handing that command, the dashboard would need to decide if the request is valid and if should be dispatched.
  • have dashboard send TaskClaimRequestedEvent with the respective Task aggregate identifier (and perhaps some extra information about the task). It does not need to know who will handle that event.
  • have Task aggregates in moduleA, moduleB, …, ModuleN that handle the TaskClaimRequestedEvent. While doing so each module would have to decide (based on the event’s payload, aggregate identifier, extra information passed, … if it should process the event or ignore it. The module that handles the event publish TaskClaimedEvent indicating state update of a task it is responsible for.
  • have dashboard listen to TaskClaimedEvent to know when given task is been claimed (and by who if needed).

Again this is from the top of my head. I haven’t tried it and I’m not sure it will work. Just giving you some ideas to experiment with.

I am inline with this idea. Milens advice is steering towards multiple canonical models that are structured as modules in Oliver’s case (moduleA, moduleB, dashboard). This implies that dashboard module can model the concept of Task as well (an aggregate?). This is converging to three separate bounded contexts (DDD pattern) now, with ‘moduleA’ and ‘moduleB’ being downstream and ‘dashboard’ being upstream for example.

In my opinion, the concept/aggregate of Task can be modeled in moduleA and moduleB, but they are different models of the same concept. The command is the API of your modules and these commands should be different across modules(at least the package name of command class should be specific to the unique module).

My 2 cents :slight_smile:

It would be interesting to discuss how we perceive the concept of modules, structure vise and vision vise.

thanks for your suggestions. We just stumbled upon CommandHandler.commandName(). This allows us to register the command handler for a customized command name which is not derived from the command’s class name.

What we can do is to add the claimingCommandName (“moduleAClaim” or “moduleBClaim”) into the TaskCreated event. Then we could have a ClaimTaskCommand which is wrapped into a GenericCommandMessage with the given commandName.

We have not tested it, but it should work I think. That’s a pretty clean solution as dashboard can send a command without knowing the receiver.

1 Like

We tried the last approach and it works like a charm.

1 Like