This is more of an architectural design question. I’m not sure if this forum is not the right place for this and I apologize for my lack of expertize which can make my scenario harder to comprehend than it should.
In a system with multiple services all using NServiceBus:
One particular service have tons of message handlers listening to multiple events (events that are also handled in other services for different purposes). Under certain conditions (which can only be determined by this particular service) it (rarely) needs to react and trigger an action. But the problem is, in order to trigger the action, it also needs information which is only known by another service.
Our previous implementation with NServiceBus 5 was to use the callback mechanism (Send().Register() in one service and Reply() in the other service using MessageHeader to pass the information). We did it knowingly this was not recommended as proper design, but it was a simple way to make our use case works.
Now with NServiceBus 7, I did a quick and dirty port and kept the same mechanism using the callback library and Request() / Reply() functions and using resulting task.Result to read the reply message response. Using the IMessageHandlerContext instance to send the Request would trigger en exception as documented, so I tried using the service IMessageSession instance instead (again knowingly that it’s wrong to use in message handlers), and was surprised that it worked just fine on our development machines.
The documentation for the callbacks clearly state:
Do not call the callback APIs from inside a
Handle method in an
IHandleMessages<T> class as this can cause deadlocks or other unexpected behavior.
And indeed, it ran 2 days successfully in production but at some point the service got in a deadlock state and stopped processing messages.
So with that tale out of the way, of course I understand doing this kind of RPC call in a message handler requesting information from another service is a flaw in our design and not how it should be made. Problem is, I can’t see any alternative.
It seems you recommend Sagas as the solution?
My issue is that reading documentation and samples about sagas, they seems a very different scenario than what I need. From what I see, Sagas wait for multiple events to combine them and then process an action. This is not a conditional follow up of a message handling. It also requires to save every data needed to complete the saga in the saga itself, which is not as convenient as keeping the context and all variables that are in my C# thread (especially in my situation where the trigger action is deep in the call stack of my service, and not a simple message handler of a few lines). And finally, it seems like I would require a complete saga for every single situations, which is a bit of a different mindset than my current method that was more of a utility function which can be re-used in multiple places.
So sorry for this wall of text. As you can see I’m a bit mixed up and would appreciate any insight on how to see things differently.