Access Employees

Communication styles between microservices

Published in by Gustavo Mauricio de Barros

Communication styles between microservices Communication styles between microservices

Introduction

I'm reading the book Building Microservices by Sam Newman, published by O'Reilly. This book covers the main advantages and disadvantages of microservices, and whether this is really the architecture you want to adopt for your system. One of the topics discussed is how these systems communicate with each other.

Building Microservices Book Cover

Cover of the book Building Microservices.

Assuming that each microservice should be as independent as possible, it may seem contradictory to say they should communicate. If we take literally what the author says about coupling between services, we should aim for as little communication as possible.

I believe it's inevitable that our services will communicate, and you probably do too if you're reading this article. I'll show the ways I learned in the book and share my opinion on them.

First, we need to understand that communications can be divided into two broad categories: blocking synchronous and non-blocking asynchronous.

Blocking Synchronous

In this category, a microservice sends a request to another and waits for a response before continuing, keeping the operation blocked during this time.

If you work in the field or have some experience, you're probably familiar with the Request-Response pattern — that's exactly how synchronous communication happens. We can use HTTP or RPC.

Don't be confused: the async and await you use in your programming language for requests may seem asynchronous, but in your system, this interaction is blocking, as await means that line must finish before moving on.

System coupling here is bi-directional — both microservices communicate. In my view, the biggest downside is that in case of failure, retrying isn't easily possible.

Non-blocking Asynchronous

With asynchronous communication, a microservice can move on with its processing without waiting for a return. This gets more complex, and there are many ways to implement it:

Request-Response

The concept is similar to synchronous communication, but here the response is received immediately, regardless of whether processing is finished. We can also use queue-based brokers in this scenario.

Event-driven

In this communication style, a service just emits the fact that something happened, and other services listen for such events and their associated data (messages).

There's low coupling between services because a broker like RabbitMQ is responsible for receiving and transmitting events. There's also debate about how much data events should share, since too much data can increase coupling and the risk of breaking changes.

Shared Data Communication

This is the most controversial approach — some argue it’s not real communication. It involves changing a table in a database shared by two microservices, which goes against the recommendation that each service should have its own database. But that’s a more complex discussion.

This communication style can also be done via shared files, for example.

In my opinion, this model is not as bad as it seems. It’s simple and serves its purpose of allowing two services to communicate.

Which One Should I Choose?

As with almost everything in IT, you probably know the answer: It Depends!

If your system is simple and you believe that retrying a request on failure is acceptable, there's nothing wrong with using a synchronous Request-Response pattern. But if retrying without user input is critical, writing requests to a queue becomes important.

I also believe we shouldn't fear change. If your system has grown and it’s time to change how services communicate, assess whether the change brings real benefits.

Make decisions based on what's best for your team (i.e., whether they’re familiar with the tech) and what supports your application's scalability — not just based on trends or personal preferences.

← Return to start