Calling an external rest api from inside of an aggregate

Hello,

I have a case where I need to do a sit reservation in external API and register that event in the EventStore.
So, a user requests our Rest API to reserve a sit, controller catches that request.

Next I need to decide:

  1. Place (inject) the WebRequest to the external RestAPI inside the aggregate. Once aggregate receives response from the external system, it changes its state and responses to the controller.
  2. Do this logic in the controller
  3. Your suggestions, please.

What is the correct way of implementing this functionality?

Thanks,

Rashid

Hi Rashid,

I’m a little new to the domain driven design world and it’s certainly a change in the way that you look at the world. I believe that an aggregate should only really know about it’s own data, it’s own state and it’s own invariants. It shouldn’t have any knowledge about how to connect to an external system.

One approach would be:-

  • Controller calls a Service
  • Service calls the Rest API
  • Service updates the Aggregate via the the commandGateway once the service returns

Alternatively, Sagas are designed to handle problems like these but I’m not 100% sure if they can run synchronously, which I understand may be your requirement.

Cheers
John

Hi Rashid,

the way I always explain it, is as follows:
What if the external systems was an Axon application as well, and had a proper command handler. What would you have done? Probably have some component, somewhere, send a command, which would end up in that component, which would provide you with a return value. Perhaps they would also send some events that you could be interested in. Would you care about their choice in aggregate boundaries? Most likely not.

But that’s not the world as it is. You can do 2 things. Just stick to their APIs and just call it how you would have done it in a non-Axon application. Or, build a little facade so that, at least from within your application, it looks as if the external system did use commands and events in its API.

In the latter case, you would build a little component that acts as a gateway toward the external system. It translates an incoming command into a Rest call and translates the response to a response of the command. Perhaps, you’re also interested in raising an event, in case a seat was properly reserved. If there is no validation you would need to do, there is actually no need for an aggregate anywhere. You would just have a component with an @CommandHandler, that has a Rest Client and the EventBus injected, and just sends Events when appropriate. Now, from the rest of your system, you can pretend the external system is a nicely designed Axon-based application with a beautiful API ;-).

Oh, the type of component that I just described has a name, in DDD: Anti-Corruption Layer.

Hope this makes sense.
Cheers,

Allard Buijze
CTO

E: allard.buijze@axoniq.io

T: +31 6 34 73 99 89

Thank you Allard for your explanation, that makes perfect sense! ) I’m new to the Axon and sometimes feel unsure in designing implementations.

Thanks John

In the latter case, you would build a little component that acts as a gateway toward the external system. It translates an incoming command into a Rest call and translates the response to a response of the command. Perhaps, you’re also interested in raising an event, in case a seat was properly reserved. If there is no validation you would need to do, there is actually no need for an aggregate anywhere. You would just have a component with an @CommandHandler, that has a Rest Client and the EventBus injected, and just sends Events when appropriate. Now, from the rest of your system, you can pretend the external system is a nicely designed Axon-based application with a beautiful API ;-).

Oh, the type of component that I just described has a name, in DDD: Anti-Corruption Layer.

I’m looking for the “best” way to call this ACL. Say the external call would take at least 3 seconds but fails in 10% of the cases, and you would need to call this external system from a saga (tracking)

The saga receives an event “A” that triggers the need to call the ACL.

  1. Send a command and wait to the ACL, in the commandHandler handle the call, publish the outcome as an event. In case an error occurs throw, so that event A get’s tried again on the saga. (Blocking the saga handling thread, for a long time)

  2. Send a command and wait to the ACL, in the commandHandler translate the command to a passive-aggressive integration Event: externalServiceCallRequestedEvent, in the ACL have a tracking event handler, call the external system there. (thus persisting intent, and ensuring handling) (localize the blocking to the thread handling the ACL-EH, size that pool accordingly)

  3. as 2 but publish the integration-event directly from the saga.

  4. ??

Basically, a tracking event handler provides async, and reliability. But I really would like a “tracking” commandHandler.

Thoughts?

rgds,

Jan

  1. have a queuebacked component that buffers and forwards…

public class QueueBackedIntegrationCommandBus {

private final CommandGateway gateway;
private final QueueMessagingTemplate messagingTemplate;

public QueueBackedIntegrationCommandBus(CommandGateway gateway, QueueMessagingTemplate messagingTemplate) {
this.gateway = gateway;
this.messagingTemplate = messagingTemplate;
}

public void publish(Object command) {
messagingTemplate.convertAndSend(“commandqueue”, command);
}

@SqsListener(value = “commandqueue”, deletionPolicy = NEVER)
public void on(Object command, Acknowledgment acknowledgment) {
gateway.sendAndWait(GenericCommandMessage.asCommandMessage(command));
acknowledgment.acknowledge();
}
}