Few suggestions for the future

Hello,

I want to say that I really like Axon Framework but there are still few things that are not so good.

First what I wish for is a way to constructor inject dependencies into sagas. I know that there is a issue for that and that it may not be that easy to do. One way I see it how it could be done is that saga registers some kind of serializer and deserializer for itself. That way a saga could be prototype bean in spring and framework would just call serializer or deserializer when it needs to.

Second thing is I do not like Annotations and would like a way for AR to register its own command handlers, event handlers and snapshot handlers. That way it would become really obvious that one AR is handling many events and it may be that it is handling too much. Same thing could be done for Query handlers.

I understand that this can not be done easily but is just a suggestion for the future.

Rene

Hi Rene,

constructor injection (or at least Spring managed wiring) of Sagas has been on the agenda for a while. The challenge is that the Saga is serialized in order for it to be stored. It is difficult (haven’t found a way yet) to deserialize an instance, but also have it initialized using Spring.
One way it could be done, is by creating a new instance (injected with resources), and load the existing instance from the repository. Then all non-transient data should be copied to the injected copy. This may work and probably will in most cases. I just don’t think it will improve performance and it feels like a very fragile solution. Any ideas on how this could be done are welcome.

Could you show me some pseudocode on how you would like your AR to register the handlers? Take into account that you can’t do this as an instance method, since you need to define command handlers before any instance is created.

Theoretically, you could already define your own way of describing the Aggregate structure. Axon uses the AggregateModel interface to access all the handlers method. The default implementation that creates this AggregateModel is one that uses annotations. In practice, it’s pretty complex to build such a model, because Aggregates can contain nested Entities, and messages need to be properly routed between them.

Cheers,

Allard

Hi Allard,

For Sagas it could be done in a similar way that snapshots are done. As I like to do things trough interfaces there could be a Saga interface with methods serialize, deserialize. That way deserialization would be decoupled from instance creation.

One way this could be made is some cind of AR registry where you would register AR class and all the command, event handlers. You can think of this in a similar way that Google Guice does with modules. A system like this could support both annotation based and code based AR registration and configuration.

This is a quick example how it could work:

`

public class BankAccountModule implements AggregateModule {
@Override
public void configure(AggregateRegistry registry) {
registry
.aggregate(BankAccount.class)
.command(CreateBankAccountCommand.class, CreateBankAccountCommand::getBankAccountId, BankAccount::handle)
.command(DepositMoneyCommand.class, DepositMoneyCommand::getBankAccountId, BankAccount::handle)
.event(BankAccountCreatedEvent.class, BankAccount::handle)
.event(MoneyDepositedEvent.class, BankAccount::handle);
}
}

`

Hi Rene,

for the Aggregate configuration, that’s similar to what I had in mind as well. That’s pretty doable, although this part is the easy part. It becomes more complex when commands need to be forwarded to entities. Also, Axon allows for additional parameters to be resolved for command handlers (and any type of handler, for that part). Would you suggest dropping that capability for this style of configuration, or referencing methods by their name and letting Axon figure out the parameters?

With respect to you Saga solution: having an interface is something we did in Axon 1, and we’re very happy to have left that structure. What I can imagine as a solution, would be to separate the State from the processing. That means not the Saga class itself would be serialized, but just an object defining its state. But then that object needs to be injected somehow as well. Not sure if spring likes “partially provided constructor parameters”.

Cheers,

Allard

Hi Allard,

At the second taught I concur with you interface in this situation feels akward.

`

public class BankAccountModule implements CommandModule {
@Override
public void configure(CommandRegistry registry) {
registry
.aggregate(BankAccount.class)
.command(CreateBankAccountCommand.class, CreateBankAccountCommand::getBankAccountId, BankAccount::handle)
.command(
DepositMoneyCommand.class,
DepositMoneyCommand::getBankAccountId,
(aggregate, command, resourceLocator) ->
aggregate.handle(command, resourceLocator.getResource(ExampleService.class, “some identifier”)))
.event(BankAccountCreatedEvent.class, BankAccount::handle)
.event(MoneyDepositedEvent.class, BankAccount::handle);

registry
.saga(BankTransferManagementSaga.class)
.event(BankTransferCreatedEvent.class, BankTransferManagementSaga::on)
.serializer(saga -> { /* extract saga state and return / })
.deserializer(saga -> { /
initialize saga state */ });
}

@Ovveride
public void initialize() {
// optional method that would be run after framework has been initialized
// could be used as initial data load or similar
}
}

public class BankAccountModule implements QueryModule {
@Override
public void configure(QueryRegistry registry) {
registry
.query(QueryRequestObject.class)
.handler(QueryHandlerClass1::handle)
.handler(QueryHandlerClass2::handle)
.handler(QueryHandlerClass3::handle)
.handler(QueryHandlerClass4::handle)
.handler(QueryHandlerClass5::handle)
.merge(MergingStrategy::merge);
}
}

`

The pseudocode I posted above could solve both issues at the same time. And the above solution could be used as a unified interface for code configuration and annotation configuration. There could be a framework provided AnnotationModule(Module is an example name because I do not know a better name at this time) which would could look into spring container(or any other container) and configure everything.

The issue of additional handler parameters could be solved with ResourceLocator. In any other situation I would say that direct usage of ResourceLocator is bad. But this is configuration not runtime code so Resourcelocator in my opinion is OK to use.

The initialize method above I am not sure if it is a good idea but I am looking for a way to initialize some data on application start and this could be a way to do it.

Hi Rene,

did you already have a look at the Configuration API? It can already do most of the things you describe. The only thing really missing is a Java-based configuration of the structure of Aggregates. If you want to do things on startup or on shutdown, you can configure handlers on the Configuration.

Also, you can already avoid having to use annotations. You can register command handlers directly on the Command Bus. Just do commandBus.subscribe(MyCommand.class.getName(), message -> {…});
The first parameter is the command name you want to handle (the fqcn of the command, by default). The second is the function to execute on an incoming command. Note that you get the command message. From here, you can load the aggregate and invoke an action on it.

The only limitation you currently have, is that you need to explicitly load an aggregate. Having a Java-based DSL to describe an aggregate/saga structure (mapping commands to specific method), it would allow Axon to automatically load commands.

This still doesn’t address the Saga serialization issue, though. I don’t like the idea of putting the responsibility of (de)serializing state in the Saga instance itself. In my eyes, it’s the responsibility of the Repository. An interesting approach I have seen in one of the .NET frameworks, is having the Event Handling in a singleton class, passing a state object into each of the handler methods. The framework could automatically find the right state instance, just like it finds Saga instances right now. The downside of this approach, is that it “promotes” separating the logic from the data that the logic uses.

Do realize that you can pass “dependencies” as parameters to your @SagaEventHandler (and @EventHandler or @CommandHandler) methods. Spring beans are automatically injected if there is a single candidate in the application context. I don’t know why you don’t like annotations, but they do have their advantages…

Cheers,

Allard

Hi Allard,

Yes I have looked trough configuration API and missed that future. I can configure commands that way but not events.

I do not like Annotations because they add something to API without beeing in the type system. I always try to express as much as I can trough type system. Only annotations that I am OK with are source level annotations and spring @Bean annotations. To me @EventHandler feels wrong.

Like I said I like the framework as a general I am just trying to help.

Rene

Hi Allard,

As stated by Allard, injecting dependencies via handler method parameters sounds like the easiest way to avoid entanglement with (de)serialization process. Both for sagas and aggregates. And that brings me to another question: why have distinct interfaces to inject dependencies in aggregates and sagas? The former uses ParameterResolvers and the latter ResourceInjectors.
I would expect the same injection strategies to be applicable to both, right?

Cheers,
Benoît

They can actually both use ParameterResolvers. The ResourceInjectors are also available for Sagas, mainly for historical reasons. Also, Sagas generally need resources to do their work, Aggregates don’t.

That’s also why it would be nice to be able to have Spring wire the Sagas using Constructor Injection. But that interferes with serialization…

Cheers,

Allard