Hi folks,
I stumbled upon an interesting issue. In an open source library I’m using Axon Framework for dispatching commands to the aggregates. Now this framework is being used for a project and they also use Axon Framework for command dispatch - not only to the aggregates but just to connect parts of the system. Everything I describe works on the same machine (no distribution, just decoupling).
Here is the call stack:
- RestController gets invoked
- Rest controller creates a command
A
and sends it via command bus - An annotated CommandHandler receives this command
A
and does some business logic - As a result, some commands
B
are collected and sent out on Tx.beforeCommit synchronization using command bus - Commands get received by another annotated command handler for
B
- Commands
B
get dispatched to an aggregate - Aggregate tries to emit events (using
AggregateLifecycle.apply
).
During the last step an Exception is thrown by the EventBus
saying: It is not allowed to publish events when the root Unit of Work has already been committed.
.
I think I understand why it doesn’t work, but I wonder how I can work around this.
In fact - the first delivery of the Command A
has nothing to do with dispatch of command B
. The application uses command bus just for decoupling of components. A positive side effect is the error propagation, but any other propagation would also do in this case…
Is it possible to create a sub-unit of work to avoid this problem?
What is so specific about the root UoW, and why it is checked in the AbstractCommandBus
:
UnitOfWork<?> unitOfWork = CurrentUnitOfWork.get();
Assert.state(!unitOfWork.phase().isAfter(PREPARE_COMMIT),
() -> "It is not allowed to publish events when the current Unit of Work has already been committed. Please start a new Unit of Work before publishing events.");
Assert.state(!unitOfWork.root().phase().isAfter(PREPARE_COMMIT),
() -> "It is not allowed to publish events when the root Unit of Work has already been committed.");
I played around trying to bypass the second assert, but whatever I’m doing, any message (command or event) dispatch opens a root UoW, in that I’m calling some business logic and only on Tx-commit (before or after), the commands are generated… So I’ll ever have the root UoW in COMMIT phase which hits the second assertion.
Is there any way to decouple UoW from the Tx? or can I just finish the UoW manually, before sendin new commands?
Any ideas?
Cheers,
Simon