I’m a big fan of Microservices.
Does it sound weird, given the title? Well, it should not.
Every now and then, a new paradigm or technology arises. It brings hope and hype. This will save us all!
Yeah, it’s true. Maybe it is ground-breaking. Extremely beneficial. However, like everything in life, every solution has its scope.
Have you ever tried to apply Newtonian laws to subatomic particles? Or, have you ever tried to squeeze a television into a sock? Pretty much the same here.
Hype makes you forget this little clause at the end of the contract. And results are catastrophic.
Splitting is the way
In a normal web application, you have a single process serving all requests. Well, you can replicate it for balancing reasons, but in any case there’s a single package/software you have to run. And it bundles all the logic together.
When you use Microservices, instead, you have several, distinct processes, each one serving just a subset of requests. Each process runs a distinct package, with just the code it needs.
Is it really new?
No, it isn’t.
The Microservice pattern is nothing new, actually, like almost every emergent design (besides it has been around for some years).
It is Object-Oriented Programming applied on a higher level: application architecture.
No coincidence that it often makes use of HTTP APIs, particularly RESTful, which are in turn OOP principles applied to API design.
P.S. I hate acronyms.
Let me explain.
When you create a program using Object-Oriented Programming, you split it into several pieces, Classes. Each Class represents a component of the solution and has a specific role. It also works just on its own subset of data. Does it sound familiar?
This is not the right place to discuss about Object-Oriented Programming in general, but I have to point out some aspects. If you are not familiar with it, you can reference online documentation and, soon, my other article about it!
The Two Principles — The Good Tailor
When you design Classes, you must follow two main principles: loose coupling and high cohesion.
To be honest, these should be the guiding stars of each software project, not just OOP ones. But that’s another story. Or is it?
High cohesion is the situation where each component is strongly consistent. All its data and methods refer to a very specific area, or meaning, or role.
Loose coupling is the situation where each component has the least possible amount of interactions with other components. It can work on its own, hiding low-level details, and exposes the essential interfaces.
For Microservices it is the same.
I can’t stress this enough. A good OOP design leads to an optimal Microservice design. There is no middle ground.
This should be almost enough to answer why and when to use Microservices. But since there is more, let’s break it down.
When to split?
This is exactly as it is for Object-Oriented Programming.
When you can respect high cohesion and loose coupling.
You know how to split code into Classes in the right way. Why would you split your Microservices randomly?
If a bad Class diagram is painful to maintain and manage, because of the sparsity of code in different files, think of randomly split Microservices application, where you have different processes altogether.
But why on Earth would I split my application into sub-processes?
Imagine how much it’s easy to make two Objects interact. You just have to call an Object’s method and you are good to go. All within a single program.
Microservices, instead, live in different processes. Possibly, on different machines. You have to communicate over a network, using APIs.
It adds complexity!
You must have excellent reasons to do so.
Not for trend, not for fun. Because, I assure you, if you use Microservices randomly, their management is not fun at all.
Actually, you must have the same reasons you’d have when you decide to use APIs. It’s the same.
APIs let you hide all the implementation details behind them. That’s essential in some cases, and it offers incredible benefits.
1. Scalability for non-uniform traffic
Scenario: you have a Supermarket application.
You have a Stock Microservice, that just shows you the amounts of articles in stock, and a Vision Microservice, which uses GPUs to detect your articles given a picture of them.
If you receive thousands of calls for your Stock APIs and just a bunch of requests for your Vision APIs, you could just replicate your Stock Microservice.
No need to double your GPU resources!
Think of it. If you kept everything in a single machine or application, and you still wanted to scale to match all the incoming requests, you would have to replicate it entirely. A waste.
2. Error resilience
Suppose you have a Bank application. Your Transfer Microservice keeps crashing, maybe for a temporary error or, worse, for a newly introduced bug (damn untested releases…).
Why should the entire Bank application be down? Maybe some customers wouldn’t even notice, because they just want to see their movements or they want to use their cards.
3. Separate deployments
Similarly to the reason above, if you have a new feature to add or a bug to fix, you can just deploy the right Microservice. Your build and deployment times should be smaller.
Also, you could put your Microservices on different machines, in different locations, maybe.
4. Complete isolation
Maybe you need complete isolation between Microservices. In this way, you can use different DBs (SQL and noSQL) or different technologies altogether. This will give you complete freedom!
And it is of course strictly related to the other points as well, about error resilience or requirements.
5. Different requirements
Do you have a heavy computation to perform? Maybe a Machine Learning one, where you need GPUs, their libraries…
Maybe that part would be much better in Python, while the other one could benefit from Java.
Or maybe libraries for the same language would create some conflicts, but you desperately need two specific versions for two distinct tasks.
And finally, perhaps you want to use two different Cloud products to host your two distinct pieces.
There are plenty of reasons, some more common than others.
In those cases, split the application!
Probably it’s easier. But remember to respect the Two Principles! Be a Good Tailor, or you would have hard times sharing data between your Microservices…
Is it not your case? Go for a Monolith.
If your system is tightly coupled, use a single application. A Monolith.
You’ll have cleaner dependency management, and you won’t need complex orchestrations or distributed systems to trace errors, share data, collect logs, sync calls, secure network interactions… and I can go on.
Is Object-Oriented Programming not suitable for your application architecture? Or, better, is RESTful design not suitable? Microservices won’t be either!
If your application serves a long sequence of API calls, maybe because an API requires the result of the previous ones, this is not ideal. It would introduce also network delays.
If your application is a single procedure that requires user interaction, well, the sentence is self-explaining. It is a Procedure!
You have to share a state between calls, each component is not independent and a single error in the sequence will block it entirely. There is hardly a benefit in splitting the application.
And remember, Monolith doesn’t mean Mess
They are also spelled differently.
Someone sees Monolith as a bunch of unordered code.
They claim that Microservice pattern is the only way to have splits, order and all.
Well, I’ll reveal something.
It’s — not — true.
Your code design does not depend on process divisions. Object-Oriented Programming doesn’t either. If you split your code right, if you manage your dependencies in a clear hierarchy, as well as your files, you can achieve the same level of cohesion and decoupling.
And another secret: if you divide your APIs into separate packages/modules/controllers/blueprints/whatever in your code, then you can decide to serve them in a single process or split them in Microservices at build-time, or at run-time too, for free.
I’m a big fan of Microservices, as I said.
If used in the right context, for the right reasons, they solve issues and improve performances or costs.
But please, stop making them the one and only solution for all problems.
Because they can also be the root of all evil.