Nowadays almost every large software is built using microservices architecture. As I always say, this introduces a whole bunch of problems within the software. One of these problems is communication between services.
The problems that you would probably face in this aspect are:
- Synchronization: synchronizing all the services so the system would eventually be in a consistent state
- Homogeneity: More often than not, services will be using different protocols or different technologies
In order to solve these problems you can use REST or RPC, however, both protocols are synchronous which means that a service is bound to block on the other service to finish handling the request before it's able to free resources for new requests. This is where messaging comes in play. In this article I will explain briefly messaging brokers and transition to embedded messaging, however, I will not get into much details about messaging protocols or patterns.
Messaging is an asynchronous way of communicating events or tasks between different services. To illustrate how messaging works, let's take a simple example.
assume that you're working on a system for a bank. the system has 2 services:
- Transactions service that logs the transactions
- Account service that handles the customers accounts
assume that a customer makes a deposit. In that case, we need to add the deposited amount to the customer's account in the account service and log the transaction in the transactions service.
so the request goes to the account service first which updates the customer's account with the new deposited amount. Then the account service would create an event with the deposit action and send it to the messaging broker. Then the transaction service would receive that message and log the transaction.
The account service's role in the process ends as soon as it sends the message to the broker which means that it no longer blocks on the transactions service.
However, we can take this one step further by using embedded messaging.
As you can see, there is no longer a connection between the transactions service and the broker, which means that we don't need to worry about them being disconnected. This also adds the possibility of different classes within the service to communicate using the broker and it allows for parallelizing some functionality by having different classes listening to the same event or message.
For example, assume that the transactions service logs the transaction in 2 different databases and sends a notification to a different service. Since we don't need to worry about losing connection to the broker; we can have 3 different classes within the transactions service listen to the same message so the 3 functionalities would start at the same time as soon as the message is received.
We can even take a step further and use embedded messaging for other functionalities replacing other protocols altogether. For example, if the transactions service needs to contact the users service to get the user's name, we can have a design that looks like this:
The transactions service then sends a new message to the broker inside the users service requesting the user's name. The message would have a unique identifier along with the payload to relate the response to the current task. Notice that the transactions service would connect to the users service's broker, send the message, then disconnect. We no longer need connections to an external intermediate service.
The users service can then send the name to the response queue in the transactions service's broker along with the unique identifier that was initially sent in the previous step (again connect, send, disconnect). Then the transactions service can carry on logging the transaction with the user's name.
Since we no longer have an intermediate service, we eliminated a single point of failure and we alleviated bottle necks associated with that intermediate service.