Why a Monolith might be better than a Microservice Architecture
In 2014 the Microservice architecture gained a lot of traction and was quickly presented as an end-all be-all solution to all problems we traditionally encountered in a monolith, both technical and cultural. During this time a lot of companies made the switch from a Monolithic architecture to Microservices, without really understanding what this meant for their teams and applications. A few years following this, around 2018, more and more articles were released talking about going back to a Monolith. These companies realized that Microservices, even though they solved some of our problems, also introduced an entire new set of problems.
Misconceptions Surrounding Monolithic Architectures
Before we take a deep dive into the Monolith vs. Microservice debate, I think we should take some time to clear up some misconceptions about the Monolith.
The term Monolith leaves a bad aftertaste for many developers. When we talk about the Monolith, terms such as Spaghetti code and Big Ball of Mud Architecture, come to mind. We seem to always talk about how everything in a Monolith is connected, with no clear boundaries or structure. In extreme scenarios, we even say that we don't have confidence to make changes to an application, because we can't guarantee that changing one thing doesn't break something else in the application.
Now to me, these are all very real problems, that most of us encounter more than we would like to admit. However, I do think that these problems are not the result of having a Monolithic architecture, but rather 'bad' design in general. I simply don't think the negativity surrounding the Monolith is actually justified.
"Saying everything that's running in a single application is a Monolith, would be the same as saying that everything that is distributed is a Microservice." ~ Bo Hanssen
The idea of building a great single application simply became too difficult, so we started to break up that application into smaller ones thinking it would be so much simpler and easier. Turns out, surprise - surprise, it mostly wasn't.
What exactly is a Monolith (in comparison to Microservices)?
We have talked about what a Monolith isn't, but what is it exactly?
Monolithic Architecture
A Monolith is a single application that is responsible for all logic to a specific domain. This means that most, if not all, functionality is written in this application. Further more, a Monolith is usually committed to version control using one repository (also known as the MonoRepo). A Monolithic application is usually is a single deployable, this doesn't necessarily have to be the case but it is very common.
Since Monoliths (usually) have only one repository and a single deployable, the Continous Integration and Deployment (CI/CD) pipelines are quite simple. The infrastructure the application runs on, although not very scalable, isn't overly complex either.
A Monolith is essentially an Integrated System, and this is a good thing. We want architects and developers to be able to wrap their head around an entire application. Having teams that can develop whole features, front to back, is extremely valuable and makes unnecessary alignment meetings obsolete. All the domain and technical knowledge is encapsulated in the team and dependencies to other teams are minimal.
Of course, the larger the code base gets and more developers join, the system will become harder and harder to wrap your head around. This will result in a steeper learning curve, especially for new developers. This is why we need our Monolith to become Majestic. More on that later.
Microservice Architecture
Microservices is an approach to application development in which a large application is built as a set of many (relatively) small services. These small services are built following the Single-Responsibility Principle, where each service is responsible for exactly one thing. In the most pure form, these microservices even have their own dedicated database. All these advantages allow teams to work autonomously, without disrupting other teams and their changes. The You Build It, You Run It mindset requires that teams have knowledge in the DevOps way of working. This is definitely something to be aware of, because without embracing DevOps, teams are going to be unable to fully take advantage of this approach.
All microservices are deployed independently. This allows for Continuous Deployments to production, as many times as we want, whenever we want. And should we face high load on our software system, we can easily scale specific Microservices at runtime (elasticity). This does mean, however, that our CI/CD pipelines are going to be much more complex in comparison to an application with a single deployable.
Since all Microservices can be pushed to production independently, we will most likely end up with a lot of versions of the same service. Managing what client should use what version and for how long it will be supported, is more difficult than migrating everyone over to a new version of a service. Since all Microservices are loosely coupled this can be quite the challenge.
The Microservice architecture relies heavily on a good infrastructure. Since most calls from and to Microservices are conducted over a network connection, dropped connections, time-outs and slow response times are things to consider when designing a resilient system. In this approach, the criticality of the business process is a determining factor when Designing for Failure.
We need to balance our system's granularity and inter-dependencies. A giant monolith is simple, but inflexible, whereas a thousand tiny microservices will be too difficult to manage and incur significant communication overhead.
The Rise and Downfall of Microservices
The Ascension of a New Paradigm
Let's take a step back to the beginning. We really want to fix our 'bad' monoliths and everywhere we look we see experienced architects and successful big tech companies talking about Microservices. This architecture is generally hold in high regards, often showcased as the Holy Grail that will help us fix all our problems. Separate services that are responsible for one thing - high cohesion - followed by loose coupling between these services sounds really good if you ask me. This basically means that the complexity of each individual microservice is easy to manage, great.
However, moving to Microservices, without changing the company culture, won't bring you to the promised land. One of the drivers to make a transition to Microservices is autonomous team and team size. In my opinion, this trade-off is made too soon, because the organization is not ready for this way of working yet. The best advice I can give is to delay this decision as long as possible. Don't try to fix a problem that you might have in the future, instead fix the problem when it arrives.
"We’re gonna break it up and somehow find the engineering discipline we never had in the first place." ~ Kelsey Hightower
It is also ludicrous to think that if we are going to break up our large application, we someone find the engineering discipline we never had to begin with. If our large application is messy, how can we guarantee that we won't distribute that messiness into our Microservices (and thus our infrastructure)?
In other words, the Microservices architecture is very promising, but organizations are just didn't have the right information when they started their transition.
The Collapse of What Was Promised
A very common mistake when moving to this architecture is defining the wrong boundaries. Instead of grouping functionality (domain partitioning) that belongs together, the grouping and slicing is done on a technical level (technical partitioning). For example taking out entire tiers of a Tier-based Architecture or an entire Hexagon out of an Hexagonal Architecture, instead of extracting part of the domain to a stand-alone Microservice.
Due to this mistake, there is a big chance that the nasty structure (Big Ball of Mud) is still there. The complexity of the MonoRepo is now scattered across many different services, that are responsible for too many things and depend on each other too much. The Monolith is basically transformed into services that house the exact same problems, only now the overhead and complexity have been moved to the infrastructure. This is also known as Distributing the Monolith. Spoiler alert! You're gonna have a bad time.
The reasons for choosing a Microservice architecture no longer apply. The infrastructure and deployment strategy is way too complex, there are too many lines connecting different services, and because of this, teams can no longer work independently.
Never. Ever. Distribute your Monolith.
In a future post, I will go into more detail on we might prevent this from happening. It has something to do with making our Monolith Majestic first. Stay tuned.