Synchronous communication means that both parties communicate directly with each other. The exchange of information happens through the established communication channel.
The communication typically starts when a client (the party needing the information) sends out a request to the provider (the party that has the information). The provider receives the request, processes it immediately and sends back his answer.
One could say that by sending a request to the provider, the client opens a communication channel and the response is sent back through the same channel.
- Client sends the request (channel is opened)
- Provider processes the request
- Provider sends back its answer (using the channel)
- Client receives the answer
There are different patterns to implement this protocol. Beside the traditional Request-Response pattern we see also new patterns emerging like streaming.
The request-response pattern is best known by everyone as it is implemented by many technologies such as REST APIs, SOAP services and the newer gRPC.
The streaming pattern is somewhat special however, as here the data is pushed to the client. In order for the client not to “lose” any data it must always be available when the provider wants to send data to it. Moreover, the Provider must be stateful because it needs to know to which clients it needs to send the data.
But all these synchronous communications have the same major drawbacks:
- A call will only be successful if both consumer and provider are online.
- Through call chaining a system becomes fragile very quickly.
Call chaining means that the completion of one specific request relies on the completion of several underlying request initiated by the service invoked by the original request.
- Call chaining also introduces unnoticed latency to the system.
- Client and server must be able to “find” each other. You need concepts such as Service registry, Service discovery etc…
Mainly because of the second drawback only use this style if the answer is really needed in real-time (be very critical if this is really the case). Or if you need the supporting functionality such as monetization, monitoring, rate limiting … which is better implemented (through the use of API Gateways) in this communication style.
In all other cases try to avoid it!
Asynchronous Communication is a communication style where the different components communicate by exchanging messages. In general, the communication between the different actors is facilitated by an intermediate component called Message Broker (not needed: see webhooks). As the communication is defined as “asynchronous” the client will never wait (block) for a response. As such provider and client can operate independently from each other. A failure in one has no direct impact on the other.
Asynchronous Communication normally consists of the following building blocks:
- Message: Structure used to exchange data/information.
A message normally consists of 2 parts. The first part, the header, this is the section of the message that contains metadata on the message. Typically key-value pairs that contain information about the message. Such as identification, type, priority etc… The second part, the body contains the actual data of the message.
Depending on the pattern used, different types of messages can be identified. Command Messages are used by the client to instruct a service to “do” something (typically one-to-one communications).
Event Messages are created by a source service to inform that something has happened (typically used in one-to-many communication).
- Channel: Abstracted representation of the communication path between the sender and receiver of a message (implemented by queue , topics etc… ). Sender will put a message on the channel, a receiver will get a message from the channel (Not exactly the same as with Sync communication).
There are different implementation patterns to achieve this communication style (Eventing, Command messages, webhooks). Detailing those is beyond the scope of this blog but we will briefly touch on the concept of eventing as this is regularly discussed in microservice architecture.
Events are messages raised by the different services in your microservice solution to indicate that something has happened. The two most common use cases for these events is to either facilitate data replication (Data Events) or to facilitate business process functionality (Business Events).
Data Events are needed when data is cached (replicated) by other services in your microservice solution. So typically, these events indicate that data was changed and it serves as a signal to all depending services to “refresh” their cache.
Business Events signal that an important action was performed in the context of a business process example: An order is created. We must now reserve stock, and ask for payment. To do this we could create an “Order was Placed Event”. The stock service and payment service could “listen” for those events and perform the necessary action when they receive it.
It is important to make a distinction within your architecture between these events. They may look the same at first, but their purpose is different, and they will deviate over time. Separating them will allow to manage these different lifecycles better in the future.
Depending on your needs the structure of your events can also differ (State Carrying Events vs Notification Events). But again, we will not dive into the details of that topic in this blog.
Regardless of which implementation pattern you choose, asynchronous communication has the following advantages:
- It makes provider and client more independent of each other.
- It provides natural buffering. The provider will not be flooded with requests (they are buffered by the message broker).
- It can be used also in sync messaging (all depends on the response time – see webhooks).
There are however also drawbacks:
- Currently there is no standard to document these “Async APIs”. However here also things are moving. Look on our site as one of my colleagues has published a blog on this topic.
- Due to the lack of standardisation, testing is cumbersome as there are no common tools. Also the problem of version management is not very well worked out. Commonly accepted best practices on these topics have not yet materialised.
- Because we are using a message broker the resilience is linked to the broker.
- Because of the broker the overall complexity of the solution increases. The microservice team is no longer in charge of all its components.
Regardless of these drawbacks, we strongly believe that the advantages considerably outweigh these shortcomings as most of them have very simple mitigations solutions.
With asynchronous communication your different components (services) no longer need to rely on each other’s presence. The solution can function even if one of the components becomes temporarily unavailable. It is because of this major advantage we recommend using the asynchronous communication style by default.