“Don’t break your toys… unless it’s to have 2 playable toys.”
A significant part of my 10-year career as a consultant in middleware and applications development has been centered on microservices architectures, still an important topic for IT modernization nowadays.
At the very beginning, it was important to just explain the concepts, what it was about and why it wasn’t just another way to speak about service-oriented architecture (SOA). Then came best practices, patterns, and pitfalls.
Some projects were greenfields, of course, but there were also many initiatives around modernizing monoliths by splitting them into smaller pieces.
After a few years, I started to hear some negative feedback. That feedback highlighted and emphasized the higher complexity of the resulting solution. People were saying that the architecture was way more difficult to manage, that end-to-end visibility was a nightmare. Eventually, I even heard some people saying it would be better to go back to the traditional monoliths on traditional application servers.
So the question that came to my mind and that I tried to figure out by gathering information about their transformation projects was: “Didn’t you get any benefits from the microservices architecture?”
That’s the question I’d like to explore with you here.
Where should I start?
Going from monolith to microservices is not an easy task. And the result will indeed probably be more complex to administer. So it’s critical that the transformation is conducted for a purpose, and that some benefits show up at the end.
A key question in a monolith-to-microservices transformation is: Where to split the monolith? How many pieces should I have—one, five, ten? And, most importantly, why?
Let’s first look at some (non-exhaustive) elements that led to an architecture that didn't bring what was promised.
With microservices, I will save resources (thus money)
No, you won't. There’s no way 10 GB can be bigger than 5 times 2 GB, which actually will easily turn into 5 times 2.5 GB or 3 GB because of the duplicated stack, e.g., the server and operating system.
You’ll achieve better resource control by switching to a lightweight, more resource-efficient runtime—for example, by going from an application server to a Spring Boot application, or to a Quarkus one. But this task will be made much easier if the application is first refactored as a set of components better fitting a new runtime environment.
With microservices, my applications will be more reliable
Not really. Five tightly-coupled services will still fail altogether as one piece.
You’ll achieve that by introducing circuit breakers, alternative flows, asynchronicity, loose coupling mechanisms, and other solutions of the same kind.
So, performing a microservices transformation with only those objectives in mind won’t lead to happiness.
Microservices approach: A structure in a structure
Even though the 2 concepts could be evaluated in isolation, we should consider the business-to-IT alignment before even thinking about microservices architectures. Business to IT alignment, on the technical perspective, comes down to the “separation of concerns” principle, which states that never two microservices share the same responsibility and that one microservice never has more than exactly one responsibility. An attempt to improve business-to-IT alignment was given by SOA architecture and domain-driven design. We’ll assume here that this part is something we can already leverage on.
The microservices approach comes on top of this to complement it. It adds technical considerations to the matter in order to provide more flexibility, with better scalability, shorter release cycle so shorter time-to-market and improved robustness with failure transparency; all of them together leading to a much higher efficiency.
The monolith breakdown pivots
Here is a proposal of 4 pivots to drive a monolith-to-microservices breakdown activity in order to see an added value at the end.
1. The scalability
A monolith should be split into 2 parts when those 2 parts have a reason to scale independently from each other. The objective here is to increase flexibility.
So, if we detect, with experience, that one technical element of an application requires more resources than another under a specific load, breaking it down into 2 microservices could here help save resources (as not the whole application would need to scale but only a part of it).
The application of this principle can lead to a rather extreme example that trades higher flexibility for higher complexity where one component is split into its read operations runtime and its write operations runtime (also known as the CQRS pattern).
2. The ability to release more quickly
This is probably the one benefit that is expected the most, tightly linked to the time-to-market problem.
This aspect can be reactive or proactive:
- It can be that we realize that, over time, one piece of the monolith was updated more often than the rest. The coupling of this piece with the rest of the application creates some friction that slows down the release process. In such a case, we can benefit from splitting down the application in 2 parts to add a decoupling.
- Or, we do want to be able to release a particular business function with a reduced time-to-market. However, this business function is embedded in a monolithic application with tight coupling to other business functions. Here, a split into microservices could allow us to insert the appropriate level of isolation for that function to be released at a much higher speed.
3. Failure impact isolation
Impact isolation is a key to application reliability.
Sometimes, statistics can show that one constituent of the monolith is more subject to errors than its other constituents. However, many errors usually lead to the entire monolith to become unresponsive.
Splitting a monolith around that pivot can be a solution to improve the robustness of the overall business service.
It will help introduce error management concepts such as circuit breakers, alternative flows or asynchronous call/callback to create loose-couple functions with fail-fast capabilities that will prevent that an error on one constituent of a service makes the whole service unavailable to users.
4. Component specialization
Although I haven’t really witnessed or haven’t been reported with this pivot as being a priority concern for customers, we can definitely say there are traces of it out there. For example, web UI interfaces and ML models tend to stick to their preferred programming languages, JavaScript and Python, while interacting with other microservices written in other languages such as Java or .NET…
I’m convinced this pivot is something whose consideration could be higher, as it can definitely provide added value. The idea behind the “specialization pivot” is to break down a monolith so that specific functions can leverage a more appropriate programming language or even full technology stack.
Conclusion
We hope the outlined approach can assist you to make informed decisions for your microservices approach or microservices transformation, and that the balance of the complexity versus flexibility will finally lean to the right side.
Last updated: January 18, 2024