Best practice for Saga Associations

Hi, I’m trying to build out a multi-service Saga POC using Spring / Axon / Axon Server. Ideally, I would like each service to manage its own AggregateId (creating it within each service, not from within the service housing the Saga Orchestrator).

I know that the Saga’s handlers are meant to listen / action events, as well as send out commands. In the examples I see, typically the AggregateId is created within that Saga handler, and then it’s passed in as an argument into the outgoing Command over the CommandGateway. That command is taken and used to create the target service’s aggregate. After the command is sent, the Saga association is made using what has become that Aggregate’s AggregateId.

Is there an Axon supported way (one that uses Axon’s messaging system) for a Saga to send a command to the target service and have it generate its own AggregateId and have it send it back to the Saga to associate it? Or maybe some other method of getting each individual service to create its own AggregateId – outside of the Saga handlers.

Thanks in advance!

Just to build onto the above… I found a stackoverflow post that Steven responded to in 2019: https://stackoverflow.com/questions/55363935/how-to-handle-commands-for-aggregates-with-ids-assigned-after-command . Basically, Steven is saying that commands that are handled on an Aggregate constructor don’t require the aggregate id to be passed in at that point. And it sounds like as long as you populate the AggregateId annotated field within the constructor you’re good to go.

When I tried to do this from within my Saga class, I started getting exceptions. The flow that I’m going for is Order/Saga service sends the command message (containing no @AggregateId field) to another service which will handle and build an aggregate using its own internal logic to create an AggregateId as a result. Unfortuantely, it only seems to be getting as far as AxonServerCommandBus. When this process is triggered, I see the Saga getting invoked and then an AxonServerCommandBus warning about a problem dispatching the command (now containing no AggregateId annotation / field), and then it throws a CommandDispatchException: The command [com.company.commands.CreateInvoiceCommand] does not contain a routing key.

Any help that anyone can give will be greatly appreciated!

I was messing around with @RoutingKey and that seems to do what I want. So what I’ve done is use the AggregateId from my OrderAggregate as the association at the start of the Saga. After that, I define each command with that same orderId and annotate it with @RoutingKey. Once the command is routed to the correct service, the service then creates its own AggregateId.

So now I’m left with these:
1.) Will this workaround create some unforeseen issue for me. I’m relatively new to Axon, so I appreciate any pointers or nudges toward “best practices.”
2.) Axon is open source right? Do you want community support for expanding / updating your documentation? There’s nothing in the framework documentation that I could see that ever mentions RoutingKey. I stumbled on it from exception analysis and looking at the api docs.

Thanks again!

Hi Philip,

Happy to see you’ve already found some information on your own to tackle the issue!
I’ll try to resolve the remaining two questions you’ve asked right now.
On top of that, I want to be very specific that this mailing list will be discontinued as of the 28th of September (2020).

Instead of that AxonIQ has provided https://discuss.axoniq.io/, to support a more forum based solution of discussing things.
Trust to see you there in the future!

Now, for you questions:

1.) Will this workaround create some unforeseen issue for me?

The @RoutingKey is only a necessity if the RoutingStrategy used by the distributed CommandBus (for example Axon Server) is configured to be the AnnotationRoutingStrategy.

This AnnotationRoutingStrategy is the default for the AxonServerCommandBus, so it does make sense you’ve uncovered this solution of using an @RoutingKey in your fields.
Note that the @TargetAggregateIdentifier annotation (required to route commands consistently to the same instance, and to define which aggregate they’re targeted at) is meta-annotated with @RoutingKey.

As such, you would only be required to use the @RoutingKey in the following scenarios if the AnnotationRoutingStrategy is used:

  1. If the command is not targeted towards an Aggregate.
  2. If the command creates the Aggregate
    Whether you are hitting situation 1 or 2 in your example isn’t entirely clear to me, but in neither scenario would I regard the usage of the @RoutingKey as a workaround.
    There is a routing requirement that commands are consistently routed to the same instance and the same command model component.
    If you want the service to be in charge of routing, you would adjust the RoutingStrategy.
    If you feel your commands require this information, than I would recommend using the @RoutingKey.
    Again, neither is wrong. It’s a matter of choice really.

2.) Axon is open source right? Do you want community support for expanding / updating your documentation? There’s nothing in the framework documentation that I could see that ever mentions RoutingKey. I stumbled on it from exception analysis and looking at the api docs.

Axon Framework, the extensions and the Reference Guide are all open source indeed.
Any contributions are always very much appreciated!
So if you are up for the task, I’d be happy to review your contributions as soon as you’ve sent them in.

Trusting this will help you further Philip!

Cheers,

Steven van Beelen

Axon Framework Lead Developer

AxonIQ

twitter-icon_128x128.png

Want to learn more about DDD, ES, CQRS and more?
Join our Event-Driven Virtual Conference 2020. Buy your tickets here.

Hey Steven,

Thanks so much for getting back to me. I want to tell you how much I appreciate reading your incredibly detailed answers here and on stackoverflow.

The more I play with Axon, the more I come to like it. I just realized that I can eliminate the commands going out of the Saga Orchestrator from Service A altogether if I set up an @EventHandler in the destination service, Service B. That allows Service B to issue its own internal commands to be processed within its service. So now I’m setting up a totally event driven flow between services: Service A (w/ Saga) builds the ThingAggregate, emits the ThingCreated event which the Saga Orchestrator and the Service B @EventHandler pick up. Service B issues its own command, and the flow continues much the same. If I follow that design, will the Saga Orchestrator continue to serve a purpose?

That’s my final question here; all of my futures ones will go on the axoniq forum.

Thanks again for your time!

Best,
Philip

Hi Philip,

Thank you very much for sharing that Philip.
We at AxonIQ aim to provide a consistent level of support, definitely for our direct clients, but also maintained through the open channels.

Now, let’s get to the follow up question you’re sharing with us:

If I follow that design, will the Saga Orchestrator continue to serve a purpose?

Yes, it does, but it very much depends on the scenario you are in.
Some use cases “scream” for using a Saga, whilst others require a more hefty tool like a BPMN, and yet others require no such thing at all.
Where the line lies between simple event handler, saga and BPMN, is very much use case dependent.

A simple guide line you can use (which by no means rules out everything mind you!) to figure out whether to use a Saga to model your business transaction, is the following:

  • Is there a notion of time?
  • Is the process relatively straight forward?
  • Do you require the maintenance of state throughout the transaction?
    If your answer to all three is yes, a Saga would be reasonable.
    If the process becomes very complex and intricate? Using a BPMN would likely be a better option.
    And if the process is simpler, has virtually no concept of time or requires no state, a simple event handler (like in your use case) would likely suffice.

Trusting this helps you further Philip!

Cheers,

Steven van Beelen

Axon Framework Lead Developer

AxonIQ

twitter-icon_128x128.png

Want to learn more about DDD, ES, CQRS and more?
Join our Event-Driven Virtual Conference 2020. Buy your tickets here.