Motivation
It's 2024, and we've long since embarked on our journey away from Monoliths—or so we've been told—embracing concepts like Service-Oriented Architecture (SOA) and subsequently Microservices. However, somewhere along this path, we seem to have lost sight of the fundamental and transient qualities and nature of sound software architectural concepts. As new talent joins our ranks, they're inundated with whitepapers from various sources, including cloud infrastructure vendors and SaaS service providers, extolling the benefits of anything but monolithic applications.
Amidst all this noise, it's become increasingly challenging to find an objective definition of what exactly constitutes a monolith. Understanding this is crucial for appreciating the alternatives and their value propositions.
I have even sifted through some of the academic journals to find if there are any clear directions on this as to how we can baseline these myriad characterizations. Here is a link. My IEEE membership has unfortunately expired.
Furthermore, the subsequent adoption of SOA and Microservices failed to clearly articulate their unique value propositions compared to one another. Instead, we witness a blind promotion of architectural styles that may, when viewed more broadly, stem from an evolutionary deficit—a cognitive mechanism geared toward energy preservation that inhibits us from looking beyond surface-level distinctions.
[Dijiang Huang, Huijun Wu, in Mobile Cloud Computing, 2018] states this phenomena as below
“In short, while both microservices and SOA began as architectures, they ultimately became movements.”
This inclination towards movements is intrinsic to human nature; we seek collective affirmation to validate our choices. While there's nothing inherently flawed about this tendency, blindly adhering to such movements can also result in making decisions without considering their suitability in specific contexts.
So what are the invariable set of criteria we could use to define a monolithic application? To answer that, we need to dive into claims and counter-claims that span humans, organizations, technology and, of course, movements.
Defining A Monolith App - Once And For All.
[Dijiang Huang, Huijun Wu, in Mobile Cloud Computing, 2018] writes as follows: “Monolithic style – a monolithic application built as a single unit. For example, enterprise applications are often built in three main parts: a client-side user interface (consisting of HTML pages and JavaScript running in a browser on the user's machine), a database (consisting of many tables inserted into a common, and usually relational, database management system), and a server-side application. The server-side application will handle HTTP requests, execute domain logic, retrieve and update data from the database, and select and populate HTML views to be sent to the browser. This server-side application is a monolith – a single logical executable. Any changes to the system involve building and deploying a new version of the server-side application.”
Let's begin with the first line: "built as a single unit." This is one of the abstract terms often employed to describe monolithic applications. However, upon closer examination within the same context, we realize that the entire application is not truly a single unit, but rather consists of three distinct parts, forming an N-tiered architecture. So what does the author really mean here as “single unit“?
Let’s further look at what [Dijiang Huang, Huijun Wu, in Mobile Cloud Computing, 2018] fields in this paper as the shortcoming of monolithic applications.
“Scaling requires deploying multiple copies of the exact same application code to multiple servers.”
“Change cycles are tied together – a change made to a small part of the application requires the entire monolith to be rebuilt and deployed.”
“Over time it is often hard to keep a good modular structure, making it harder to keep changes that ought to only affect one module within that module.”
“Scaling requires scaling of the entire application rather than parts of it that require greater resource.”
They are all valid in some take or another. Yet, none of these characterizes what a monolithic application really is.
Here is another article on AWS that talks about monolithic applications and microservices. I would like to draw your attention to how monolithic and microservices are depicted on this article. Here is a recreation:
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff87e18d0-2699-42bc-91e5-3d2c9bfcf3e0_556x387.png)
(A) Let's consider the boundaries in the diagram as representing "logical decoupling." If that's the case, could we not enhance the modularity of the monolith to maintain this logical decoupling?
(B) Alternatively, if the boundaries indicate "physical decoupling," then could we not deploy duplicates of the monolith on different servers?
Reflecting on these questions will help us dive deep into these topics.
The Myth Of A Monolithic Monster
The comparison between "microservices" and "monolithic" architectures is frequently portrayed as a battle between good and evil, with microservices hailed as the superior alternative. However, this subjective narrative is more common in commercial whitepapers than in academic discourse.
To illustrate the fallacy of these comparisons, let's consider the following analogy.
Pagodas will always be pagodas
In this analogy, we'll be using Pagodas as an example. Pagodas, renowned for their remarkable stability, are constructed to endure earthquakes and withstand the forces of nature, ensuring their longevity across millenniums.
In the realm of software architecture, envision a multi-tiered pagoda as analogous to a well designed, layered modular monolithic software system. Each floor of the pagoda represents a domain function, akin to tiers in the software system, all housed under one roof.
Now, suppose the head monk seeks to scale up the operations on a specific floor (let's call it floor k) by allocating more resources and personnel. To achieve this, the function on floor k must be relocated outside the main pagoda.
Where this function from the main pagoda relocates will essentially become another pagoda, albeit perhaps only with a single floor. Scaling this further would become a matter of building another pagoda with single floor but does the same function as this one.
An additional observation lies in the increased number of walkways between these pagodas. These walkways serve as an analogy for remote service invocations over a network, facilitating communication between different components or services.
This is an important realization. If you for example, take a microservice implementation, it is not so different than a monolithic that got refactored from.
Monolithic Characteristic Invariants
Based on the materials presented in this paper, we can draw the following inferences and conclusions about the prevailing true picture of a monolithic software.
Inferences
Software can be service and yet be a monolithic. Therefore being a service is neither a sufficient nor necessary criteria to declare a software as monolithic or not.
One can easily create any number of services for a monolithic application and even scale it by deploying copies of the same monolithic application. Which functions of the monolithic behind a service point get invoked can be controlled by the trickeries in a Layer 7 gateway that sits in front all service interfaces. This is a common pattern prevalent in the early days of cloud migration and even today.
Software can be modular and yet be monolithic therefore being modular is neither a sufficient nor necessary criterion to declare a software as monolithic or not.
For example, a service that gets refactored from a monolithic could still be implemented in a non-modular way. Consider a Nodejs environment that powers a service.
The software can be tiered and layered in architectural style yet be monolithic.
Therefore N-tiered and layered architectures are perhaps necessary but not sufficient criteria to declare a software as monolithic or not.
A “functional failure domain” of software may not necessarily change when it gets decomposed into independent services.
Any achievable degree of decoupling is, in turn highly subject to the “degree of functional dependency“ between the decomposed units. A service may be technically decoupled from another but may remain functionally tightly coupled. Because such dependency is instilled by the inherent nature of the target “business domain” that the software serves and not by any design or technology choices. For example, in Figure 1.0, the “Thread Microservice“ has a business domain level dependency on “User Microservice“ and “Posts Microservice“ and will fail in the same fashion as if it is a monolithic application if either of these services is down. In this case, perhaps, running copies of a monolithic instance (which in turn will have copies of these service interfaces) may offer better resiliency, albeit with other tradeoffs and shortcomings.
Inter-module or inter-process communication modes are neither sufficient nor necessary criteria to declare software as monolithic or not. Such communication is perhaps a prominent characteristic necessity of software. The modes of this communication can be a direct method invocation, a remote method invocation, or a service call over a network. For example, two monolithic applications or services could indirectly communicate via a common database or local network ports.
The manner in which software units are developed, often independently and by teams with scalable autonomy, may seem distinctive, yet it is neither a sufficient nor necessary criterion for determining whether a software system is monolithic or not. For instance, various platforms supporting technologies like Enterprise Java Beans (EJB) and Dynamically Loaded Libraries (DLL) allow for the development, testing, and release of features fairly independently.
A common misconception is that monolithic architecture mandates a single codebase, which is not necessarily accurate. In reality, each module can exist as an independent codebase, with dependencies injected either at build time or runtime as compiled modules. Examples abound, from the modular structure of Node.js to the flexibility offered by the OSGi framework for Java.
The deployment method of a software system is undoubtedly important, but it alone is neither necessary nor sufficient criterion to classify a software as monolithic or not. For instance, feature flags enable control over which modules are loaded into runtime, allowing for the deployment of multiple copies of the same "software executable" while maintaining resource efficiency. Furthermore, modern cloud-based deployment strategies are agnostic to the internal architecture of the software, further demonstrating that deployment method alone does not dictate monolithic nature.
How a software gets operationalized is perhaps a necessary but not a sufficient criteria to identify if a software is monolithic or not. Even worse, operational blindness and troubleshooting efforts all will increase as the software gets decomposed presenting additional challenges, introduction of auxiliary systems and services etc.
Conclusions
Here are few conclusions to consider.
The objective of software refactoring should not solely be centered around "de-monolithicizing." Even when restructured into a collective form, such as services, the software will inevitably maintain elements of its monolithic nature across various tiers and layers of refactored units. However, this inherently monolithic nature is not necessarily detrimental; it can still serve functional and architectural purposes effectively.
The primary objective of refactoring software should prioritize the restructuring of its business domain logic into highly decoupled and autonomous "isolation domains" across all tiers and layers. These "isolation domains" should closely align with the delineation of "business domains" identified during design exercises . Such exercise should not about creating services or monoliths.
The "degree of isolation" among domains (across business functionality, technology, operations, performance, and failure handling) within the layers and tiers of the software serves as an abstract gauge of the level of decoupling and independence achieved by the refactored units of the software. Consequently, this measure inversely correlates with the degree of monolithic nature inherent in the software.
It is not evil.
Being monolithic is the most natural state of a software solution.
It is on a spectrum
The degree to which a software embodies a monolithic architecture spans a broad spectrum encompassing its functional domains, business and technical architectural tiers, and layers.
“Isolation domains” is the invariable criteria
The true measure of a software's monolithic nature lies in the "degree of isolation" among domains within its layers and tiers. Specifically, assessing the level of monolithic characteristics within the software's functional domains provides a clear indication of its constraints.
Forget not the goal
The objective of refactoring a monolithic software should focus on restructuring its (business) functional domains rather than its software stack.
Excellent read and a lot of food for thought. I particularly appreciate the way you have tried to explain the situation by using the phrase "degree of isolation" and "it's on a spectrum".