Posts

Microservices vs monoliths: which approach is better for your project?

Jun 13, 2024
Dmitry Chuyko
12.8

Microservices are still a buzzword despite the fact that they have been around for more than a decade. But curiously, the tables are turning, and microservices are increasingly mentioned in a rather unflattering context.

This article is not going to ride the “microservices are evil” wave, nor will it be an anthem for microservices architecture with a “break up your monolith for exceptional scaling and resilience” tune. 

Instead, we will look into strengths and weaknesses of both approaches and discuss how to choose between them depending on your project, plans, and capacities. We will also discuss an alternative solution that could serve as an intermediate step between the two.

Regardless of the approach, choosing a right base image is essential to keep cloud costs at bay. BellSoft develops Alpaquita containers for Java applications based on Liberica JDK and Alpine-compatible Alpaquita Linux that can help you save up to 30% RAM!

What is a monolith?

A monolithic application (or simply, a monolith) represents a single code base that includes all application layers: presentation, business logic, data access, etc. All these components are packed and deployed as a single unit. 

Monolithic architecture

Monoliths used to be a traditional way of building applications, but this approach should not be considered old or worse, obsolete. Monoliths are suitable for small to medium-size applications that do not process large volumes of data. As user requests slash through a single ‘box’ right through to the database, the response times with monoliths are usually fast (and in the case of smaller data sets, even poor-quality SQL queries don’t affect the performance tangibly).

The problems start when the application grows, new entities are added, new libraries are integrated, etc. Then the code turns into a tangled spider web, where a small change in one part of the system can lead to failures or unexpected behavior in a far corner of the application (the ripple effect). Spoiler alert: microservices do not magically solve the problem of bad code.

In addition, scaling doesn’t come easy for monoliths, or rather, it doesn’t come cheap because you have to scale the whole application instead of several most critical parts.

In broad terms, pros and cons of monolithic applications can be summarized as follows.

Pros and cons of monoliths

Advantages:

  • Ease of development: Monoliths are easy to develop (at least, in the beginning, before introducing complex business logic), containerize, and deploy.   
  • Simple to debug and test: As monoliths function as a single system, they are a good fit for integration testing, and it is also much more convenient to debug them because all application components are tightly intertwined and right at your fingertips.   
  • Minimal latency: communication between application parts is seamless and fast, which contributes to shorter response times. 

Disadvantages:

  • Not resilient: As all components are stuffed together in one tight box, the failure of one component may take down the whole application.
  • Harder to scale: It is difficult to scale one part of a monolithic application without touching the rest, so you may either end up with performance bottlenecks or with huge cloud bills for scaling the whole app. 
  • Difficult to update: The ripple effect mentioned above makes it harder to introduce changes to the application without breaking or changing the existing components.

What are microservices?

Microservices architecture is an approach to building software as a set of independent services that can be deployed and maintained separately. Microservices communicate with each other over the network via language-agnostic protocols, so they can be written in different languages and use different technologies. Adding new features is easier than with monoliths because the addition of new services doesn’t break the normal operation of the existing ones. It also doesn’t necessitate the redesign of the wholly system, making release cycles shorter.

Microservices architecture

The true power of microservices lies in scaling. When particular services are overloaded, you scale them horizontally or vertically without touching the rest: this way, you maintain optimal performance of the application and pay for the cloud resources you actually use. 

But with great power comes great responsibility. Establishing the right connections between all microservices, whose numbers can reach hundreds or thousands, is no small feat. Testing the whole system with standard methods is nearly impossible. Plus you should do something with the common codebase: should you create a common library (and how do you update it) or keep the microservices stuffed with boilerplate? And that is only the tip of an iceberg. So if you don’t think the whole system through at the very beginning, you may end up with a monstrous mess of a system nobody understands or controls.

Besides, a badly written monolith is one problem. Badly written microservices are the same problem multiplied by a number of services. So bad code broken down into microservices doesn’t turn into gold. It is still bad code, only you get even more problems specific to distributed systems.

Pros and cons of microservices

Advantages:

  • Scalable and flexible: Individual services can be easily scaled horizontally to adapt to the current loads. In addition, it is easier to implement new features without impacting the existing codebase.   
  • Resilient: If one service goes down, others will continue functioning, allowing for high fault tolerance.
  • Multi-language: The degree of service independence and isolation is so high that microservices can be written in various programming languages and using various frameworks and solutions, which enables the developers to use the advantages of multiple technologies for business benefit. 

Disadvantages:

  • Hard to debug and test: While writing unit tests for individual services is nothing new, it is extremely difficult to test and debug the application on the whole.
  • Additional latency: Services have to communicate with each other over the network, which leads to increased latency. 
  • Maintenance and development complexity: When writing microservices, developers have to decide which functions have to be separated, how to establish the connections between the services, how to update them in a unified way, etc. In addition, introducing changes is not as easy as it may sound, as teams have to be aware of the whole system layout, i.e., which service does what.

Microservices or a monolith: how to choose?

It’s tempting to throw in the usual “it depends.” But in reality, even if you lean towards one approach at the start, you may change your mind later.

For instance, you have a small application and you are not planning to conquer the global market. However, somewhere along the way the business took a turn and started rapidly expanding. A monolith as it doesn’t meet user requirements any more, new functionality must be added. In addition, at some point, it may get flooded with requests, and your team is terribly overloaded trying to meet the SLAs. Separating several “hot” components from a monolithic application into separate services might be a good choice in this scenario.

But don’t rush into the microservices jungle without considering the complexity this approach brings and whether you have the capacity to deal with it. Or rather: do the benefits of microservices architecture outweigh the complexity of maintaining them? After all, there are not so many giant enterprises where microservices will shine in all their glory. And considering that some of these giants such as Shopify do quite well with a monolith, and some of them like Uber migrate from microservices to macroservices admitting that microservice maintenance is too complicated, microservices don’t seem to be a “must-have” of a successful business. 

So choosing between microservices and a monolith boils down to counting and comparing pluses and minuses of both approaches for your use case, much like using the Descartes Square technique. 

Descartes Square to choose between microservices and a monolith

Still can’t decide? In this case, there is an optimal middle ground — a modular application.  

Modular applications: a structured monoliths ready for microservices

Modular architecture is the approach that focuses on dividing the application into smaller parts called modules, which are isolated from each other but have one codebase nevertheless. In other words, you still have a monolithic application, but its parts are not as intertwined as in a traditional monolith. Modules can be added or updated without affecting other components. In addition, communication between modules happens through API or events.

With modular application, you leverage benefits of monoliths and microservices:

  • Communication between modules is fast,
  • Modules are isolated from each other, facilitating addition of new features,
  • The application structure is clearly defined,
  • Modules can be easily separated into services without running the whole app.

As a result, modular applications can be considered a bridge between microservices and monoliths. So if you have a monolithic app, try breaking it down to modules instead of tearing it apart into microservices right away. If you develop Spring applications, there’s a great new project called Spring Modulith aimed at helping you build modular apps. 

And if at some point you decide that microservices will benefit your business more, you will have no problem dividing modules into services.

 

Subcribe to our newsletter

figure

Read the industry news, receive solutions to your problems, and find the ways to save money.