Publishing events (across JVMs) but not having them stored in the event store.

I have a use case where every hour I have a job which is scheduled to run.

The job is not attached to any aggregate - the code that runs sits in two separate JVM processes that share an Axon Server instance:

  • So we have JVM (a) and JVM (b).

The steps are essentially:

  • JVM (a): Publish ‘instruction’ event instructing that the job needs to run (once every hour).
  • JVM (b): Receive ‘instruction’ event, query an external API to retrieve a whole load of data (~5mb), publish event with data that is retrieved.
  • JVM (a): Receive event with the data and perform transformations on that data (further filtering, publish more events (for JVM (a))), etc.
  • The point here is to transform the outside data into a list of ‘create/update aggregate commands’.- JVM (a): Create/Update a number of aggregates (by issuing commands).

Now aside from what is highlighted red, I do not want any of the events which are published as part of this job to be stored in the event store:

  • For my use case having information about the job being placed in a log rotated file is fine.

  • In this regard, Axon event publishing is being used more to logically organise code rather than for event sourcing/event storage.

  • Of course, for the bit in red, given that aggregates are being created we would want those events to be stored like normal.

My question is what would be the best way to organise this code for my use case.

If I publish an event, currently it will always go into the event store.

I note that if I were using Query and QueryHandler then the logic could potentially be broken up as described and it would not be stored in the event store.

Please advise,

Regards,
vab2048

Hello,

There is only one implementation of EventBus that is distributed across Axon Server: AxonServerEventStore
This implementation implements EventBus and EventStore at the same time, so this implementation will always store events to the event store (Axon Server).
Maybe Axon Server needs AxonServerEventBus implementation (implementing only EventBus interface), I’m not sure :slight_smile:

There is an option to distribute events (SimpleEventBus) over JVMs with Kafka or Rabbit. You can have a look at these extensions if you like, but I think you should consider changing your design rather then doing this.

Best,
Ivan

Thank you for your reply Ivan.

I will go forward by changing the design so that all of which I mentioned earlier which is not in red is done using HTTP requests between services (instead of by publishing events).

That way I do not need to include dependencies on Kafka/Rabbit.

Regards,

No problem,

You can also send Commands (other than http requests) FYI if that fits your requirements better. Commands will be routed over the Axon Server.
Axon Server is able to route three types of messages: Commands, Events, and Queries. Events are not the only choice.

Best,
Ivan

Using commands instead of events actually makes more sense semantically.

I have done a quick test but commands require a @TargetAggregateId when my target is simply a ‘CommandListener’ @Service not an aggregate.
As a result I get the exception:

Command ‘…’ resulted in org.axonframework.axonserver.connector.command.AxonServerCommandDispatchException(The command […] does not contain a routing key.)

Is there a way to indicate to route a command to a @Service on another JVM?
If not then I’ll stick to HTTP requests.

FYI - I’ve seen a somewhat analogous issue on the github page which has been targeted to 4.4 (https://github.com/AxonFramework/AxonFramework/issues/1121).

Perhaps my use case could also be fed in there to help shape the feature.

Thanks,

Using commands instead of events actually makes more sense semantically. -> Yes it is, in this case particularly.
So, either stick with requests or use commands which you can handle in external command handlers on another side (without the formal introduction of Aggregates itself). This (by using command) will allow you to utilize Axon Server as a broker (and service discovery) and it will enable usage of Aggregate pattern on the side where you have a service now (command service can be replaced with aggregate in the future)

Thank you for your help so far Ivan.

I have seen the documentation page for the ‘external’ command handlers here: https://docs.axoniq.io/reference-guide/implementing-domain-logic/command-handling/external-command-handler.

However, it is still not clear what to put as the @TargetAggregateId on the command so I can get it routed to the other JVM which has the @CommandHandler annotation on the method.

I get the error:

Command ‘…’ resulted in org.axonframework.axonserver.connector.command.AxonServerCommandDispatchException(The command […] does not contain a routing key.)

Are there any source code/github repos you can refer me to which illustrate this pattern?

Thanks,
vab2048

Hi :slight_smile:

Try to mark any attribute that you think can serve as some sort of identifier (it does not matter, you don’t have a real aggregate listening on the other side, yet ;)) with @TargetAggregateId annotation. Please mind that this identifier will be used by Axon Server within a consistent hash algorithm to determine to which node of your application (if you have more instances of this app running) to send the command. You get load balancing out of the box.

I’m not really deep into your implementation, but with this design, you will be closer to redesigning the jobs you have at the moment to the real aggregate/s in the future. Axon Framework has the concept of deadlines and schedules …

Hope this helped!
Take care,

Ivan

Thank you for your help. I got it to work by following your advice.

I created a field in the command class called, ‘routingKey’ and attached the ‘@RoutingKey’ annotation to it.
I also gave it a default value in the class itself so call sites which create an instance of the command do not need to specify a value for the routingKey field (the default value I gave was simply the int 1).

Regards,
vab2048

Hi all,

I just like to chime in in regards to the routing key exception you are getting vab2048.
The framework has two annotations you can use when it comes to routing command message:

  1. @TargetAggregateIdentifier
  2. @RoutingKey (FYI, the @TargetAggregateIdentifier is meta-annotated with @RoutingKey)

As this command is not targeted towards an aggregate instance but rather a service, I’d suggest to use the @RoutingKey annotation as that more closely aligns the use case.

There is another argument I’d like to add into the mix though.

The object in charge of deciding “what the routing strategy is” in a distributed Axon application, is the RoutingStrategy.

By default, the AnnotationRoutingStrategy implementation will be used, which looks for the aforementioned annotations.

Now here comes the other pointer to think about:

Maybe, the command you want to dispatch to the external command handler does not have something you’d call a routing key at all.

Thus, you just want it to be handled by “a service”, no matter which.

In that scenario, you can also configure the AnnotationRoutingStrategy with a differing UnresolvedRoutingKeyPolicy.

By default the AnnotationRoutingStrategy uses the UnresolvedRoutingKeyPolicy.ERROR option.

This can however be adjusted to RANDOM_KEY and STATIC, if you so desire.

Lastly, the default is ERROR so that to remind users that you should desire commands to be routed consistently.
This would minimize the changes of a given aggregate to be loaded onto for example three different instances of the application, thus minimizing reads from the event store and allowing effective use of aggregate caches too.

Hope this sheds some additional light on your options vab2048.

Cheers,

Steven van Beelen

Axon Framework Lead Developer

AxonIQ

twitter-icon_128x128.png