Hi,
I have the following situation:
My application contains three modules: ModuleA, ModuleB and Dashboard. (Starts like a joke, hmm? )
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
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