Not all communication will be synchronous request/response with HTTP/RPC or asynchronous messaging within a system. But how do you choose between Synchronous vs Messaging? Well, it depends on if it’s a command and/or a query as well as where the request is originating from. If you want reliability and resiliency, then use messaging where it’s appropriate.
Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.
The most common places you’ll encounter making synchronous Request/Response calls are to 3rd party services, infrastructure (like a database, cache, etc), or from a UI/Client.
Communicating asynchronous most often happens between other internal services that your team or another team owns within an organization. Another good approach is to use asynchronous messaging to your own service or monolith. Check out my post on using Message Driven Architecture to decouple a monolith for more.
In B2B, a common use case for asynchronous communication is EDI where you’re exchanging files via mailboxes.
Commands & Queries
One way to distinguish where to use Synchronous vs Messaging is if you’re performing a command or a query. A command being a request to change state and a query being a request to return state.
However, when you’re communicating between internal services, I recommend communicating asynchronously using a Message Driven Architecture via Events and Commands.
This means your services are communicating via a Message Broker to send commands to a queue or publish messages to a topic.
The reason I do not recommend HTTP to communicate between services is from the complexity of dealing with latency issues, availability concerns, resilience, difficulty debugging, and most importantly coupling.
Check out my post on REST APIs for Microservices? Beware! for more info on why you should avoid it as the primary way to communicate between services.
An important distinction besides commands and queries in the Synchronous vs Messaging debate is where the request originated from. For example, a client UI/browser sends an HTTP Request to our Web API Backend service, which then makes a synchronous call to a 3rd party service.
But what happens when that 3rd party service is unavailable or is timing out? What do we return to the client UI? Do we just send back the Client UI an error message? Is the 3rd party service is critical to your application/service, does that mean as long as it’s unavailable, your service is also unavailable?
Take this exact same situation, but change the originator to be a message broker, the implications are very different.
The synchronous call from our app/service was caused by a message from a message broker and the 3rd party service is unavailable, then we have many different courses of action.
We can do an immediate retry to resolve any transient errors. We can implement an exponential backoff, where we retry and wait a period of time before retrying again. You can also move the message to a dead letter queue that will allow you to investigate and manually retry the messages once you know the 3rd party service is available again.
You have many different options. Check out my post on Handling Failures in a Message Driven Architecture for more info.
The point being is that you aren’t losing work that needs to be completed, nor are any users going to be aware that is potentially an issue.
To accomplish this for the above example, we simply need to change from synchronous to asynchronous at some point through the call path.
This means that our Client UI will still make a synchronous call to our app/services, however instead of calling the 3rd party immediately, we’ll enqueue a message to the message broker and return back an immediate response to our client UI.
Then we will consume that same message asynchronously from the message broker and complete the work that needs to communicate with the 3rd party. If there are any failures, we now have the ability to handle those failures with different resiliency and fault tolerance and we do not lose any work that needs to be done.
A good example of how this applies in real applications is when you’re in the AWS EC2 Console. This would apply to many different services with any cloud provider. I’ll use AWS EC2, which is an AWS Virtual Machine service.
If you have a running instance and you choose to stop the instance, it doesn’t immediately stop.
When you click the “Stop Instance” menu option, the browser doesn’t sit and wait for the HTTP request to finish. The request is fairly quick and then your browser displays that the Instance state is “Stopping”.
This is because of the exact example I illustrated earlier where the request from the browser was synchronous, but the actual work being performed is asynchronous.
Synchronous Messaging: When to use which?
Not all interactions can be done this way. If the end-user expects to immediately see their changes, then forcing an asynchronous workflow will prove challenging. If you can provide the user with the correct expectation about long-running processes then you can leverage events to drive real-time web.
Check out my post on using Real-Time Web by leveraging Event Drive Architecture.
Developer-level members of my CodeOpinion YouTube channel get access to the full source for any working demo application that I post on my blog or YouTube. Check out the membership for more info.
- Event Choreography & Orchestration (Sagas)
- Event Based Architecture: What do you mean by EVENT?
- CodeOpinion YouTube Channel