AggregateLifecycle.apply() in the command handling constructor is handled asynchronously

I’ve ported my code from Axon 2.x to 3.x and faced a problem, for which I was unable to find a decent solution.

My command handling constructor creates couple of child entities and then executes some method on them, thus it depends on their existence.
The problem is, that AnnotatedAggregate does not have its aggregateRoot property set until AggregateRoot’s constructor returns, so all events aren’t handled immediately, but are delayed until constructor returns, so I cannot rely on certain state exists in the constructor itself.

I currently solved the problem with calling my child entity methods in andThenApply(), but code looks ugly and unreadable, because these methods can fire from 0 to many events, so I have to return null from andThenApply()

It could probably help if it would be possible to set AnnotatedAggregate.aggregateRoot from inside aggregate’s constructor, but frankly I don’t like this solution as well.

What troubles me with a new way of writing aggregates, is that it puts even more limits on the design of aggregates and entities. Now I cannot easily write unit tests to test my POJO properties and methods without passing ugly callbacks to “container”, AnnotatedAggregate. One of important DDD promises was to avoid mixing infrastructure code and domain logic, and now Axon requires writing fair amount of infrastructure code to access every wrapped domain method, and what’s worse, infrastructure puts serious limitations on how domain logic is written.

Hi Dmitry,

the goal in Axon 3 was to reduce coupling between the domain model and the framework even further. This is important feedback for us. However, the approach Axon 2 had was also very limiting in certain areas.

Regarding the andThenApply(), we have been debating on whether we should have an “andThen” (either instead of, or next to the andThenApply()). In your case, it sounds like an andThen() would help you better, since you can decide for yourself, based on input howmany apply() calls you want to perform.
The fact that apply() aren’t executed immediately inside an aggregate is an unfortunate result of using Constructors. We can’t get a reference to the instance until we have completed the constructor. Therefore, any messages passed to apply will only be invoked after completion of the constructor. The “andThen” and “andThenApply” code is executed after the constructor completes.

It’s true that the static apply method puts some extra requirements on how the aggregate is being used outside of the scope of Axon. We didn’t see any possibility to remove this entirely, yet. If you have ideas, please don’t hesitate to share them.
Could you also elaborate on how you feel this design “puts serious limitations on how domain logic is written”?

Thanks!
Allard

Thanks, Allard

Hi Dmitry,

the goal in Axon 3 was to reduce coupling between the domain model and the framework even further. This is important feedback for us. However, the approach Axon 2 had was also very limiting in certain areas.

Just curious, haven’t you considered proxy generation approach, like used by JPA to wrap entities ?

Regarding the andThenApply(), we have been debating on whether we should have an “andThen” (either instead of, or next to the andThenApply()). In your case, it sounds like an andThen() would help you better, since you can decide for yourself, based on input howmany apply() calls you want to perform.

I’d probably vote for andThen(Runnable r), as more universal, especially that delayedTasks is already a queue of Runnables

The fact that apply() aren’t executed immediately inside an aggregate is an unfortunate result of using Constructors. We can’t get a reference to the instance until we have completed the constructor. Therefore, any messages passed to apply will only be invoked after completion of the constructor. The “andThen” and “andThenApply” code is executed after the constructor completes.

I think it is serious enough special case to at least mention in javadoc and reference guide.

It’s true that the static apply method puts some extra requirements on how the aggregate is being used outside of the scope of Axon. We didn’t see any possibility to remove this entirely, yet. If you have ideas, please don’t hesitate to share them.

You can either pass aggregate to apply() as a parameter or have some possiblity to access current lifecycle and set aggregateRoot - currently it’s not possible, static method getCurrent() is protected, and aggregateRoot field is private.

For me better alternative would be to let aggregate root to implement interface with default methods, something like an example below:

public interface Aggregate {

Serializable id();

default void apply(EventMessage<?> event) {
handle(event);
}

default void handle(EventMessage<?> event) {};

}

public interface AnnotatedAggregate extends Aggregate {

static Map<Class<? extends AnnotatedAggregate>, AggregateModel<? extends AnnotatedAggregate>> models = new HashMap<>();

@Override
@SuppressWarnings(“unchecked”)
default void apply(EventMessage<?> event) {
final AggregateModel metadata = (AggregateModel) getMetadata(getClass());
metadata.publish(event, this);
getEventBus().publish(event);
}

static void registerMetadata(Class aggregateClass, AggregateModel model) {
models.put(aggregateClass, model);
}

@SuppressWarnings(“unchecked”)
static AggregateModel getMetadata(Class aggregateClass) {
return (AggregateModel) models.get(aggregateClass);
}

default EventBus getEventBus() {
return CurrentEventBus.getInstance();
}

}

If I’m right, metadata should be static, that is per aggregate class, rather than per instance. Not sure about event bus, but getter above can support all cases: per instance, per context and singleton event bus, if overridden.

Could you also elaborate on how you feel this design “puts serious limitations on how domain logic is written”?

I meant callbacks you now need to create aggregate and access its methods. Any asynchronous callbacks make debugging and stack traces more complicated to read, can promote memory leaks if objects are captured in closures, only allow to use effectively final variables, and in general promote less OOP and less readable code, something DDD is fighting against.

Hi,

yes, we have considered using proxies, but they come with an array of issues and problems of their own.

The andThen() solution is part of Axon 3, which was released last week. That should surely improve the code needed to guarantee ordering (in the constructor).

Regarding the interface with default methods, I don’t really see how that would simplify things. You will still need the framework to interfere when “running” your model. Passing the Aggregate Root to the apply() method was something we have considered, but that would only really help when you’re applying form the aggregate root itself. Otherwise, all entities within the aggregate would need to maintain a reference to the root. Something that, again, restricts modelling freedom.

We don’t pretend that you can run your model without Axon. Honestly, I don’t think it makes much any sense to aim for that. However, we do want to prevent Axon to impose certain restriction that make certain problems harder to model.
Note that the restrictions around apply() are only valid when using Event Sourcing. But in that case, it’s Event Sourcing itself that imposes those restrictions anyway. The constructor is the only unfortunate situation where this doesn’t work.
If you’re not using Event Sourcing, you can use any method you like to publish your events. Injecting the EventBus into your @CommandHandler methods is one of your options.

But with all that, we’re still open to any suggestions on improving the separation of framework and model…

Cheers,

Allard

Hello Allard

To me, dealing with all the complexity of CQRS/ES is only justifiable, when we have complex enough domain logic and state model. The core goal of DDD is to have domain logic separated from infrastructure as much as possible, so that it can be unit tested in isolation from infrastructure.

In fact, for event sourced aggregates the only infrastructure needed is eventbus, which is easily mockable. Not event sourced aggregates even do not require event bus.

Currenlty with Axon I cannot unit test my aggregates state in isolated unit tests. But also I in reality cannot test them with fixture available, because it is too limited. Complex enough state cannot be restored from snapshot and requires a lot of events in given(). I cannot perform arbitrary code in then() part, like checking current state of my aggregate, or calling its methods; I can only validate events published. Even checking events is problematic, because I need to know ids of all child entities created during command handling, but ids are created randomly, so I cannot properly craft events to expect them.

Being unable to test domain logic is very serious flaw of framework as of me, despite having a lot of great features otherwise.

I basically see no other way than rewriting for myself this part of framework, hopefully it is not very large one, and remaining part will still be useful. I actually tried to extend fixture for v 2.x of framework, but it is not designed for extension, I had to use fair amount of reflection to make properties accessible.