One-to-one relationship: handling one command instance by one handler instance

I came across two quotes by Allard (see below) which seem contradictory to me.
I could run some code to test possibilities, but decided to do it later and clarify it more on conceptual level first.

Just to make it clear, by “aggregate instance” I mean loaded aggregate. My understanding is that, because aggregates have identity, they are supposed to be “singletons” suggesting only single live instance of it to be loaded at any time on any node within distributed system.

“Command instance”, on the other hand, is a value object (or even has no concept in DDD at all and considered as CQRS-type implementation detail only). What I mean is that it can actually be represented by its multiple clones of itself across all JVMs participating in the system (with special measures to avoid duplicate processing if such command is not idempotent).

Quote 1: “It is possible to do as many actions on as many aggregates as you like. But if scalability is a big concern, it’s good practice to have a 1 to 1 relation between command and aggregate. The reason for this is that the command handler must load all aggregates into the same machine. With scalability, that means you need to prevent other machines from changing those aggregates in the meantime… the problems are obvious. […] So if you have a “composite command” (one that executes on multiple aggregates), there is really no limitation to do so. […] So Axon won’t be limiting you there…”

https://groups.google.com/d/msg/axonframework/dp-Az4pZO3I/DTIxh3hmSgwJ

Quote 2: “Note that for each command type, there may only be one handler! This restriction counts for all handlers registered to the same command bus.”

The quote is from section “3.3.3. Annotation based handlers” in the reference:

http://www.axonframework.org/docs/2.4/single.html

At glance these quotes conflict one with another.

I can try to disambiguate one case: the 2d quote is obviously refers to command type and handler type (and not possibly multiple instances of them). So, at this point it means that there could be multiple instances of the same type of command which are handled by multiple instances of the same type of handlers.

However, annotation @TargetAggregateIdentifier results in single aggregate id (not multiple) and, therefore, single handler destination. It suggests that each individual command instance can only be processed by specific single handler instance. Maybe it is only applicable when handler is an aggregate and not an arbitrary class with methods annotated as @CommandHandler.

Assuming C is command type, H is non-aggregate handler type, and A is aggregate handler type, there could be multiple scenarios (or modification/mixture of them) imagined how instances (numbered below) may interact:

Scenario 1: single command instance is processed by single aggregate instance and many other non-aggregate handler instances.

|-> H1

C1 --|-> H2

|-> H3

|-> A1

Scenario 2: single command instance is processed by any number of handler instances regardless whether they are aggregates or not.

|-> H1

C1 --|-> H2

|-> H3

|-> A1

|-> A2

|-> A3

Scenario 3: each command instance is only processed by single handler instance (aggregate or non-aggregate).

C1 ----> H1

C2 ----> H2

C3 ----> H3

C4 ----> A1

C5 ----> A2

C6 ----> A3

Question 1:

If @TargetAggregateIdentifier directs command to only specific aggregate instance (with specific id), how can there be multiple aggregate handler instances processing it (as described in quote 1)? Is it because of non-aggregate handler instances?

Question 2:

So, what scenarios are possible?

Question 3:

Are non-aggregate handler instances even normal/common cases in practice? Or is it usually applies to only some special cases like Saga pattern?

Question 4:

Hi Alexey,

Allard’s 1st quote says you can act upon as many aggregates as needed with a single command but all will happen in the same command handler. Although not required that command handler method would most likely be defined in a separate class. I guess that is what you refer to as a ‘non-aggregate handler’. This command handler would then be in charge of loading the required aggregate instances. The @TargetAggregateIdentifier annotation is pointless here. This settles your question nr 1.

On question 2: only scenario 3 is possible.
On question 3: most likely not so common because it is considered good practise to target just 1 aggregate instance with a command.
On question 4: Scenario 3 is what you should aim for: 1 command for 1 aggregate instance. If this is too strict for your use case, I see 2 possible causes. Cause 1: some constraint in your business requires 2 aggregate instances to be in sync / consistent. Probably these aggregates are not well modelled or they should be merged. Cause 2: the command can actually be split in 2 (or more) distinct commands, each targeting a single aggregate instance.

Cheers,
Benoît

Benoît, thanks for your answer.

> “Although not required that command handler method would most likely be defined in a separate class. I guess that is what you refer to as a ‘non-aggregate handler’.”

Yes, by “non-aggregate handler” I meant classes which, for example, do not extend AbstractAnnotatedAggregateRoot (or otherwise make Axon think they aggregate-related).

There is at least one thing left unclear to me. Step by step…

What led to my original questions were the sentences like this (again, quote from reference):

http://www.axonframework.org/docs/2.4/single.html#command-bus
“Each Command is always sent to the exactly one command handler.”

One way command instance targets their loaded aggregate instances is by @TargetAggregateIdentifier field annotation (runtime value of that field as aggregate id). Even within single JVM, different aggregate instances (with different ids) can be loaded into different command handler instances (in case of aggregate command handler class). In fact, this allows command handling to be run in parallel in different threads – each pair of (command instance, command handler instance) in its own separate thread. This should be reasonably scalable because aggregates tend to be independent by data.

However, what I noticed is code snippet in the reference doc (section “3.3.2. Subscribing to a Command Bus”) which subscribes only single command handler instance:

http://www.axonframework.org/docs/2.4/single.html#d5e447

// to subscribe a handler:

commandBus.subscribe(MyPayloadType.class.getName(), myCommandHandler);
// we can subscribe the same handler to different command types

commandBus.subscribe(MyOtherPayload.class.getName(), myCommandHandler);

In my own words this looks like this:

There is only one object instance (command handler instance) which can possibly be subscribed to all instances of specific command class on a given command bus instance.

That means all commands (in case of single JVM) should be processed sequentially (because there is single subscribed object = single command handler instance) to avoid competing for use of this object.
I continue question numbers to avoid confusion…

Question 5: Am I right about sequential-only processing? How would in-JVM concurrency be achieved then?

Actually, now I guess I’m partially answering my own question (because I found AsynchronousCommandBus while writing it)…

Question 6: Is it only sequential in case of SimpleCommandBus? Should I use AsynchronousCommandBus instead to get concurrent execution?

Question 7: But then, even if AsynchronousCommandBus is used… Where does AsynchronousCommandBus get additional (pool of) command handlers instances (e.g. to load different aggregate instance by id in case of aggregate command handler class)?

Regards,Alexey

Hi Alexey,

Question 5: Am I right about sequential-only processing? How would in-JVM concurrency be achieved then?

No, you are not entirely right. Actions on the same aggregate instance (same aggregate id) are always handled sequentially. Basically, when a thread loads an aggregate from the repository it receives a lock for that aggregate instance. In other words, the synchronization is on the aggregate instance, not the command handler instance.

Axon does nothing to guarantee that non-aggregate command handlers handle commands sequentially. In other words, these command handlers aren’t locked in any way and can handle many commands concurrently (including commands of the same type). Only when handling a command involves loading of an aggregate instance that is being used in a different thread will the command handler block until that aggregate instance gets released.

Question 6: Is it only sequential in case of SimpleCommandBus? Should I use AsynchronousCommandBus instead to get concurrent execution?

Question 7: But then, even if AsynchronousCommandBus is used… Where does AsynchronousCommandBus get additional (pool of) command handlers instances (e.g. to load different aggregate instance by id in case of aggregate command handler class)?

Commands are handled sequentially by an aggregate instance no matter which command bus you use. The only difference between the simple and async command bus is that the former handles the command in the publishing thread while the latter creates a new thread to handle the command.

Rene

Hi Rene, Benoit,

Rene, I read your explanation and understood the mismatch:

  • You meant “aggregate instances” and sequential processing per aggregate id.
  • I meant “command handler instance” and (potential serialized) calls for command handling through such instance.

Now I figured it out (read on).
Thank you both!

In fact, Bonoit already clearly stated it before (I just didn’t get it):

“I guess that is what you refer to as a ‘non-aggregate handler’. This command handler would then be in charge of loading the required aggregate instances.”

Sorry for using “instance” word excessively, I’m just trying to be pedantically explicit.

Understanding the confusion

I wrongly differentiated “non-aggregate command handler instance” and “aggregate command handler instance”.
There is no such difference.

I assumed that the object subscribed to command bus can actually be an instance of class derived from AbstractAnnotatedAggregateRoot (with methods annotated as @CommandHandler) - Axon’s easy way to have class for “aggregate instances”. Instances of this class load “aggregate instance” and “resurrect” them in JVM for the sake of handling “command instance”.

What was wrong?
In order to subscribe to command bus, an object should have implementation of CommandHandler interface. And AbstractAnnotatedAggregateRoot does NOT.

By the way CommandHandler interface and @CommandHandler annotation are different things, but it does not matter much in this post.

How confusion initially looked like

As you remember, I also mentioned (copy-and-paste again) code snippet from the reference doc (section “3.3.2. Subscribing to a Command Bus”) which subscribes only single command handler instance:

Reference Guide
// to subscribe a handler:
commandBus.subscribe(MyPayloadType.class.getName(), myCommandHandler);
// we can subscribe the same handler to different command types
commandBus.subscribe(MyOtherPayload.class.getName(), myCommandHandler);

And that’s what puzzled me in case of asynchronous command bus (notice logically conflicting statements):

  • There is only single instance subscribed on command bus (per specific class of commands).
  • There can be multiple “aggregate instances” (each for different aggregate ids) of the same class which are in the process of asynchronous handling triggered by different “command instances”.
    In other words (conflicting statments rephrased):
  • Command bus holds reference to single object (single command handler instance).
  • There multiple “aggregate instances” (each aggregate with different aggregate id has its own object).

Details which confirm everything

This time I did my homework with slightly extended quick start example to go through with debugger.

The answer is simple - again, there is no “aggregate” or “non-aggregate” “command handler instance”.
There is just “command handler instance”.

This “command handler instance” is implemented by Axon itself and indirectly dispatches command instances to associated “aggregate instance”.

The stacktrace (highlighted lines discussed below):
ToDoItem.(CreateToDoItemCommand) line: 28
GeneratedConstructorAccessor11.newInstance(Object) line: not available
DelegatingConstructorAccessorImpl.newInstance(Object) line: 45
Constructor.newInstance(Object…) line: 423
ConstructorCommandMessageHandler.invoke(Object, Message) line: 105
AggregateAnnotationCommandHandler$AggregateConstructorCommandHandler.handle(CommandMessage, UnitOfWork) line: 310
AggregateAnnotationCommandHandler.handle(CommandMessage, UnitOfWork) line: 277
DefaultInterceptorChain.proceed(CommandMessage<?>) line: 63 DefaultInterceptorChain.proceed() line: 69 AsynchronousCommandBus(SimpleCommandBus).doDispatch(CommandMessage<?>, CommandHandler) line: 127
AsynchronousCommandBus(SimpleCommandBus).doDispatch(CommandMessage<?>, CommandCallback) line: 103
AsynchronousCommandBus.access$001(AsynchronousCommandBus, CommandMessage, CommandCallback) line: 40
AsynchronousCommandBus$DispatchCommand.run() line: 94
ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1142
ThreadPoolExecutor$Worker.run() line: 617
Thread.run() line: 745

  1. The top instance is different every time debugger stop at the breakpoint.
    These are the loaded “aggregate instances”.
  2. The bottom instance is always the same (the same object).
    This is the actual single “command handler instance” subscribed for commands on command bus. It keeps on loading “aggregate instances” and calls there methods annotated as @CommandHandler.

To summarize, “aggregate instances” cannot subscribe to commands on command bus because there are possibly many of them. Instead, there is intermediate single “command handler instance” subscribed - it loads and calls “aggregate instances”. Of course, class for “aggregate instances” could possibly implement CommandHandler interface itself and subscribe to command bus, but then all concurrency (introduced by asynchronous command bus) would need to be handled in this class as well.

Why did I even need all these details?

It started as the need to make architectural choices - how to scale, what to worry about, etc.

As you can see, the AggregateAnnotationCommandHandler implementation provided by Axon should be “concurrent enough” because all calls to “aggregate instance” go through single instance of this class.
I don’t have any concern about this commonly used implementation, but if I didn’t know how it works, I would have no certainty in case of other issues.