How to use axon behind an (already existing) rest-api

Hi everybody,

what we would like to have is a classic rest-service that can handle a command
and synchronously return the resulting events as a result.

I already found this:
https://groups.google.com/forum/#!topic/axonframework/GAflgNQjbZo

We already have an existing frontend which can’t deal with websockets or the stomp protocol.

So my key takeaways from the above posings are basically:

  • You can’t hide axon framework behind an already existing rest api.
  • You have to be able to handle the resulting events from a command in your frontend asynchronously
  • or you can let the frontend poll against the query part of the api.

We don’t want to have CQRS primarily but love to use the Eventsourcing part of axon.
Would there be a scenario to use this part of axon behind a rest-api?
Are there any examples for this using Axon3?

Regards,
Dirk

Hi Dirk,

in fact, all three items you mention are actually possible. However, the best results are achieved when embracing eventual consistency instead of trying to work around it.

If you use the SimpleCommandBus and SimpleEventBus, it is fairly simple to create an API where the published Events are visible to the sender of a Command. You can simply assign them to a ThreadLocal List of Events in an Event Handler and read them back out in the command publisher.

However, the issue in this, is that you now depend on specific implementation of the CommandBus and EventBus (or EventStore). Changing the implementation to something that works asynchronous, will break this behavior.
Alternatively, you could also produce the events as a return value of your command. It’s not the most beautiful design, but at least it helps to get the job done.

Cheers,

Allard

Hi Allard,

thanks for your help! Your second option sounds promising.
To use your EventStore mechanism I have to produce the events in the aggregate like so, right?

apply(new SourceBankAccountDebitedEvent(id, amount, bankTransferId));

This way the eventhandler inside the aggregate gets called, e.g.:

@EventHandler
public void on(MoneySubtractedEvent event) {
    balanceInCents -= event.getAmount();
}

I don’t get it how this would fit together if the events were created outside the aggregate.
Is there an example for this somewhere?

cu,
Dirk

Hi Dirk,

Firstly, it’s custom to use the @EventSourcingHandler annotation for event handling functions in the Aggregate. The @EventHandler annotation is generally used for other event listeners.

Second: What do you mean exactly with events which were created outside the aggregate?
Do you mean events which were never applied within an aggregate, or events which were applied by another aggregate?
Either way, it should not be necessary to handle events within that aggregate which aren’t meant for that aggregate.

If you want to handle events outside the aggregate to update the query side of your application however, you could in, for example a Spring app, have a bean with @EventHandler annotated functions.
If you’re using the auto-config set up of Axon 3, it should automatically pick up that bean as a event handling bean, making it so that the events which are applied in an aggregate can also be event handled by that bean.

Hope this helps!

Cheers,

Steven

Hi Steven,

I was referring the sentence of Allard:

“Alternatively, you could also produce the events as a return value of your command”

That was what I meant with:

“I don’t get it how this would fit together if the events were created outside the aggregate.”

An example of Allards suggestion would be useful.

To Stevens information about using @EventHandler:
Adding an @EventHandler annotation would get access to the Events, but I would still be
not able to return them as a direct response to my command, right? And that would be something I would like to have so that e.g. my REST-Service
is able to return a result synchronous.

So what I basically would like to have is:

  1. Request gets to my REST-Service.
  2. My REST-Services fires a Command to Axon.
  3. As a response I get all Events that have been fired in response to my Command.
  4. My Rest-Service returns the Events the requester.

What would be enough for us would be something like this (without using commands at all):

  1. Request gets to my REST-Service.
  2. My Service calls an Aggregate function.
  3. The aggregate function fires all necessary events.
  4. The aggregate function return these events to my service.
  5. My Rest-Service returns the Events to the requester.

Maybe both is not what Axon was designed for, so we have to implement this on our own or go with Allards solution
and attach the events to ThreadLocal…

Hi Dirk,

in you @CommandHandler method, you’re probably "apply"ing events. My suggestion was to put those events in a List (or array, or any other collection that suits your needs) as well, and then return that list as a result. That means you can leave the @Event(Sourcing)Handler completely out of the equation for this scenario.

I have to say it’s not the most elegant approach, conceptually, because it ties the command handler to the specific needs a client has. But it may very well be the simplest solution that works.

Hope this clarifies it.
Cheers,

Allard

Hi Arash,

I’d say that’s a correct assumption.

The Write Side of your app, thus the one publishing the command, is also the side publishing the events (as that’s were the aggregate lives).

It is thus aware of the events being published and being returned to the component publishing the command.
If your UI/Front-end is the one directly publishing the command on the CommandBus, than yes your assumption is correct that it would need to know how to deal with those events.

That is however an assumption you’re not directly tied to.
You could just as well have a Service in between which does the translation from the events to a view the UI/Front-end can work with.

It is not the nicest approach however, like Allard already shared, so if you can refrain from directly returning the events to your command publisher, that would be a cleaner approach.

Hope this clarifies it for you Arash.

Cheers,

Steven