Good and Bad Monolith
It is unfortunate that monolith has become a dirty word. In fact, a physical monolith is typically the right thing to do. Pure evil is monolithic thinking.
After several years of the microservice hype, it now seems monoliths are cool again! Does this mean we have learned a lesson?
I guess at least we accepted what Neal Ford stated in his book Building Evolutionary Architectures:
If you can’t build a monolith, what makes you think microservices are the answer?
The disturbing question is: Why is monolith synonymous with a bad design for some and yet the right thing to do for others?
Two Kinds of Monolith
The reason is, there are two different kinds of monolith: physical and logical. While one is mostly a good thing, the other is pure evil.
The first kind of monolith is what we usually picture under the word: a physical block of software, typically running as a single process.
A physically monolithic system is developed and built as a single artifact, deployed all at once and falling down in its entirety. Resources such as a database are often shared, communication is local, interprocess.
The opposite is then a distributed system composed of multiple physically independent components each running in its own process. Each component owns its resources and communication is performed in a remote manner.
A physical monolith is no anti-pattern, it is a good thing to start with as it is easy to build, deploy, operate, and reason about.
Physically monolithic applications are pretty performant as there are no additional overheads in communication. Cross-cutting aspects are much simpler because no special platform (such as a service mesh) is needed.
As the system gets bigger, further partitioning is possible. A popular option is to apply “satellite” architecture where bottleneck services are separated from the monolithic base.
Monolith with satellites
The second kind is a logically monolithic system. Other names used are Big ball of mud, Spaghetti code, etc. Logically monolithic codebases lack boundaries (technologies are not service boundaries!), everything is coupled to everything and no visible architecture is to be found.
Logical monoliths are evil and dangerous constructs that cause high complexity and tight coupling of building blocks making development expensive and error-prone.
Logically monolithic software is unmaintainable on a scale and exponentially corrodes.
Modular and Distributed Monoliths
By doing things right or very wrong, you will end up with one of two types of systems: modular or distributed monolith, respectively.
Modular Monolith (Modulith)
The opposite of a logical monolith is a modular monolith (or, if you like, modulith). In a modular codebase business capabilities are worked out by services with explicit logical (not necessarily physical) boundaries.
A modular monolith is probably the best architectural approach for most applications. It is easy to extend, maintain, and reason about.
That means, moduliths are really cool!
Modular monolith (modulith)
Although the logical and physical natures of monoliths are independent, they often go hand in hand. That is why people easily confuse them with each other.
As the boundaries in monolithic codebases are typically not physical it is easy to cross them. A monolithic codebase, therefore, requires great discipline.
A logically, but not physically, monolithic system is called a distributed monolith. Distributed monoliths have all the drawbacks of distributed systems with almost none of the benefits.
While dealing with the Big ball of mud is a pain, distributed monoliths are a real disaster.
Systems often end up as distributed monoliths while adapting the microservices approach incorrectly.
From Microservices to Monolith
In the last years the microservices movement promised us a lot. The catch is, microservices focus only on physical monoliths, not logical ones.
To understand why we have to take a closer look at what microservices really attempt to solve.
A microservice is a service with some technical additions (independent development cycle). It is important to note the word “technical” — as the logically monolithic design is obviously a logical, not technical, issue, there is nothing microservices could potentially do for us!
Microservices propose a solution to tackle physical monoliths only. That is the reason so many attempts to build microservices failed badly, simply because the wrong issue was addressed and the true problem only got bigger (and, even worse, distributed).
With the logically monolithic design, microservices do not come to the rescue, more the opposite.
We can think of microservices as a specific approach to Service-oriented architecture (SOA).
There are several definitions of SOA, but we will focus mainly on the concept of a service, because it is the most significant. I use this modified service definition from Udi Dahan:
A service is the autonomous unit of logic for a specific business capability.
Now, it is obvious why microservices as such cannot really help us with the logically monolithic design: monolithically designed microservices are no services at all, they are mere physical components!
Business alone has the key to defining our services correctly. Only well-designed services can tackle logical monoliths and profit from the microservices approach.
It is a hard task, but Domain-driven design can help us a lot!
If you struggle with your monolithic system, the problem may likely lie in its logically monolithic design.
The physical nature of the monolith is usually a secondary problem, easy to solve after the proper service-oriented design has been applied.
Once the logical monolith is resolved, microservices architecture is just one step further...
Types of systems by physical and logical architecture