[axonframework] Re: Distributed Command bus design implementations

In terms of designing sagas for best performance, I’ve found the main thing is to keep them small. This is usually pretty easy for a saga that’s managing the operation of a single aggregate like Benoit describes, less easy if the saga needs to coordinate the activity of a number of aggregates. Whenever possible, prefer lots of little sagas over a small number of big ones.

Sometimes you really do need big sagas that coordinate activity across a bunch of otherwise independent aggregates. In my experience, getting good performance out of a big saga boils down to storing as little data as possible. Just what it needs to do its work, no more. Any non-transient fields in the saga class need to be serialized and deserialized for every event, at least in the worst case, and you don’t want to waste CPU cycles decoding and encoding data you’ll never need. Don’t store each aggregate’s full details in the saga if you can do everything you need with just the aggregate ID, for example.

One not-always-obvious thing to watch out for is that data requirements can change over the life of a saga. You might need a particular chunk of data at the beginning of a workflow but not later. In that case it can be beneficial to release the saga’s copy of the data once it’s been used, e.g., by removing it from a hashmap or nulling out the field that’s holding a reference.

Profile first. You don’t want to spend effort optimizing something that is not a significant bottleneck.

-Steve

Thanks for the insightful information Benoît and Steve.

As I was formulating my initial question I already had in mind that I would need to use a Saga. Although, I didn’t want to clutter my already long question with more information.

So indeed I was planning on this situation:

  1. Service A receives a request on a particular endpoint
  2. Endpoint in A sends a CreateCommand
  3. Aggregate X in A handles the CreateCommand and applies a CreatedEvent
  4. Saga in A handles the CreatedEvent

From here on is where I am trying to put the pieces together. Unfortunately, the last 2 weeks I’ve been busy with things I didn’t want to be :slight_smile: and haven’t had the time to experiment. So here are the steps I’m not so sure of.

  1. Saga in A sends DistributedCommand
  2. Aggregate in service B handles the DistributedCommand and applies a SomethingHappenedEvent

Assuming this reasoning is correct, how does the DistributedCommand go from A to B? Benoît hinted at that “(eg. via REST call)”. But would Axon be responsible for that call? Or that would have to be implemented through code?

Hi Bruno,

Your DistributedCommand boils down to:
5a the Saga calls an endpoint of Service B
5b the endpoint of service B translates that a Command B targetting the aggregate of service B

If the Command B is valid, the endpoint of Service B should return a ‘OK’ answer. (Eg http 200 for rest calls)
Else, it should indicate a failure

6 the saga acts upon the response of endpoint Of service B. If case of failure the saga could then either rollback to a previous state or possibly schedule an event to retry steps 5a and 5b.

Note: is the scenario above the event processing in the saga and the remote call happen in the same thread. This could reduce the throughput (not sure if that is a problem). The benefit is that complexity is limited because the remote call is synchronuous and the saga can immedialty act upon the response

Cheers,
Benoît

Hi Benoît,

Ok, so I see we would have to programmatically call endpoint B from A’s Saga.

Would it be a decent approach to have SomethingToDoInBCommand in a common lib shared by A and B, so that A would send the serialized command in the body of the API request to B and Spring would take care of deserializing into SomethingToDoInBCommand in B?

Endpoint in B:

@RequestMapping(path = "/someEndpoint", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public Callable<SomeResult> doSomething(
        @RequestBody SomethingToDoInBCommand cmd) {

Hi Bruno,

You could. But do realise this implies the domain model of service B is leaking (service A knows about the exact command of another system). When using REST endpoints I would rather suggest to map this command to a meaning full URI. eg. …/aggregate-collection/{aggregate-id}/action with request or body parameters.

Cheers,
Benoît