How To Integrate Microservices
In order to know how, we first need to ask question, why do we integrate Services.
We integrate Services as we want to access external data:
"Fetch current wallet balance".
Or because we want to Command other Service to do action:
"Transfer money to electronic wallet"
Other Services may also take action on their own, when specific Event happens:
"When Person Account Was Banned, block his electronic wallet"
Command and Events produces Actions in the system, which result in data change or side effects (like sending email/sms).
Queries on other side are only to fetch the data, they do not change the state of the Service anyhow.
It's important to distinguish Queries from Actions (Command and Events), and not mix them together. We will see way does it matter just in a minute.
Integrate Services over HTTP
The first possibility to integrate is using HTTP. We are making HTTP Request to specific endpoint and we get the Response.
HTTP power lies in getting the feedback (Response) right away.
However HTTP Integration has few drawbacks:
- We need to depend on Service being up and running. In case service is down, we are unable to call it
- Service may be overloaded or simply working slowly, which may creates latency issues on our side
- It creates coupling between Services
Even considering above problems, possibility to get the response right away makes it good fit for Query Integration.
On other hand, HTTP integration for Actions may create inconsistencies in the system.
Suppose that after registering user we call another Service to send email.
What will happen, when the Service is down?
We will not be able to call, and due to that we create series of things that need to be solved:
- If we want to keep registration, we will need to silent the error.
- If we want to send the email anyway, we will need to store failed data so we can retry it
- We will need retry mechanism with delays, as the Service may be up and running in 10 seconds or 20 minutes.
Suppose another scenario where we call another Service in order to subtract money from the wallet and the request times out.
What does this mean for us?
Well we can't really know, because Service could performed the Action, but also could not.
If the system does not provide some kind of deduplication mechanism, if we will resend the request, we may subtract the money twice.
And, if we don't we may end in situation, where money was not subtracted at all.
Well not really a good situation to be in, right? :)
HTTP is good fit for Queries, however for handling Actions there is more robust solution, Messaging.
Integrate Services over Messaging
The Messaging is like Postman, and we are like clients sending letters (Messages).
The letter contains headers and content (In Messaging we call content - Payload).
We are providing letters to Postman, and Postman delivers them to recipient's box.
When the recipient is ready, he takes the letter out of the box, reads the headers and content and perform the Action.
To push it to programming level, the Postman can be RabbitMQ, we connect to it to provide Message to deliver. RabbitMQ knows how to deliver it to given box (Queue).
When it ends up in Recipient's queue, he may consume it in the best moment for him.
Let's check how does Messaging solves HTTP drawbacks:
- HTTP Problem #1: We need to depend on Service being up and running. In case service is down, we are unable to call it
We give the letter to Postman and recipient take it out of the box, whenever he is ready.
This allows us to send letter stating "send sms to X" to Notification Service (Recipient) even if this Service is down.
When the Service will get up, he will pick the letters perform the actions.
With Messaging we become independent of the state of Services around
- HTTP Problem #2: Service may be overloaded or simply working slowly, which creates latency issues for our side
When we call the Service over HTTP we are in request-response model.
This model creates latency, as we are waiting for the response to be received.
In Messaging, we are in fire-and-forget model. This means we only connect to the Postman (Message Broker) to send a message and the flow continues, no matter of the current Recipient's state.
This set us free from the response times of other services.
- HTTP Problem #3: Coupling with other Services
If client registered in our system, we may want do several actions in other services.
Like creating a wallet in Wallet Service, sending an Email using Notification Service.
If we integrate over HTTP, we will need to call those Services in order to inform them about new registration. This of course brings all HTTP drawbacks, multiplied by amount of Services we call.
In case of Messaging, we only integrate with Postman.
We would publish Event Message using Postman and any Service that is interested may subscribe to it.
Recipients may join and disconnect from subscription, whenever they wish. The Postman will take care of delivering the message to all subscribing Services.
Messaging creates decoupled solution, where each Service may be in control of what they want to know (subscribe to).
Actions as Commands or Events
In Messaging everything we send is a Message, however we may distinguish two types of Messages depending on how we want to use them.
Commands are way to send Message to concrete Service in order to perform action.
For example, when we send Command to Notification Service stating "Send Email To Johny Bravo", we expect it to be delivered only to this Service, as other Services may not provide such action.
Events are way to publish Message, so Services that are interested can subscribe to it.
For example, we may publish Event stating "Order Was Placed".
We do not say it to any concrete Service, we are just stating the fact of what just happened.
And any Service that it willing to take action based on that fact, may subscribe to it.
In Messaging we don't depend on availability of the other Service, so it's hard to expect the response. This creates nice decoupling, however makes Messaging hard to use for Queries.
Messaging is great for Actions, as it's reliable and stable. It help us keep the consistency between Services.
In next article we will see how to apply this theory in practice using PHP and Ecotone Framework.
Click here to go to next article.