Allard,
Thank you for answer. Using Sagas makes sense but I wonder how this could be practically implemented. As far as I understand Sagas are also executed through the CommandBus which means they use the same thread (in case of Disruptor implementation) as other command handlers. So if I want to make synchronous call from Saga how could I ensure that it won't block the thread?
One of the possibility that I see is to call method on Service domain object in a separate Executor (in different thread) and then raise event from Saga (e.g. RemoteRequestSentEvent). After Service complete the blocking call it could raise event RemoteRequestResponseReceivedEvent (or RemoteRequestResponseTimedOutEvent) and then Saga will listen to this event and issue appropriate command on the aggregate to continue long-running process. I have couple of questions on this approach, though. First, where would such events be stored? I assume they would not be stored to EventStore as there is no aggregate to associate them with except for Saga itself. I want to make sure that there is no case possible to lost the events and block the Saga from progress as a result. Second, is there a more elegant way to achieve execution of remote service call in separate thread (i.e. whether Axon provides this out of the box)?
Another approach might be to raise event from Saga stating that it's ready to get additional data from remote endpoint and then have dedicated EventHandler to make blocking call. In this case it's not clear where to raise the request completion event. Can it be raised from EventHandler itself? In the Axon trader example there is remote service invocation from handler but it's done in fire-and-forget style which doesn't work in my case.
After thinking more about requirements of clients of my APIs I figured out that some of them would require synchronous request-response semantic for execution of some commands. I know that this is very bad idea to do this for potentially long-running processes so my idea is to "cheat" a bit. My REST controller will accept the request, create temporary response queue in my middleware, put request into the input queue for component (specifying output queue for response) and do the blocking read from output queue (with timeout, of course). This way the client would be waiting until timeout occurs (or response is prepared) and the thread will be blocked but at the same time component itself would still be processing all messages in asynchronous manner. I would then have dedicated EventHandler for ConversationCompletedEvent that would put the result into the temporary output queue specified in message and thus will unblock waiting thread in my REST façade. The benefit of this approach is that I could do additional arbitrage of incoming requests, so, for example, if another client wants to execute the same command while it's already in progress I could just alter configuration for EventHandler dynamically to send response upon completion to another temporary output queue instead of initiating same command over again. And some very impatient clients that send lot of requests for a same command could be cut off completely when façade will figure out that they would use more threads than allowed.
Do you think this approach for handling synchronous-to-asynchronous translation has any particular flaws or could be improved using specific techniques in Axon that I'm not aware about? Unfortunately I don't have option in many case to convert clients to the proper publish/polling model of interaction.