Many technologists today believe that microservices is the best way to build large-scale, complex systems. But what if it’s not?
Despite its popularity and widespread adoption, microservices isn't perfect. It poses several challenges around development coordination, deployment, configuration, distributed logging and tracing and reliable communication. So much so that a modular monolithic architecture may be a better alternative, especially during the early stages of software development.
What is a modular monolith?
Modular monolithic architecture is a way of organizing a software application into a set of modules.
These modules have specific functionality, which can be independently developed and tested, while the entire application is deployed as a single unit. There might arise a situation where a few modules need to be extracted for reasons such as scale. A modular monolithic application will enable this with minimal effort.
To the left: An example of the modules in a typical e-commerce application
To the right: An example of modules that can be independently deployed when the need arises
If we take the example of a typical e-commerce system illustrated above, each hexagon represents a module. During the holiday season, when the platform is expecting high traffic, and inventory and order modules need to scale, they can be deployed independently. At the end of the season, they can be merged back into a single-unit deployment.
How is it different from conventional monolith?
The main distinction between conventional monolith and modular monolith is that the latter is made of well-defined, independent and extractable modules, each of which possesses a certain business capability boundary. When modules grow in size and complexity, requiring different resources and deployment, it becomes easier to move to microservices architecture because:
It already specifies the ideal boundary of a microservice from a domain-driven design standpoint
Each module could be an independent microservices candidate in the future
The interfaces may later evolve as contracts between independently-deployed microservices
Why start with modular monolithic architecture?
Fewer moving parts to begin with
At the beginning of any project, teams are still learning about the domain and the larger ecosystem. If the project is begun with a microservices approach, as the teams’ understanding evolves, there will be a direct impact on the microservices that are built.
Instead, with a modular monolithic design, there will be few moving parts, which makes it easier and quicker to accept modifications. Once the team has a firm grasp of the business domain and its bounded contexts, they can move to microservices.
Simpler to create, deploy and manage
In the microservices approach, each service must be deployed independently, which increases the technical and operational complexity. Moreover, each service may need to be handled individually, which increases the DevOps team's burden.
In a modular monolithic architecture, the modules are produced and delivered as a single unit, which makes the application significantly easier to design, deploy and manage.
Better functionality and stability
Microservices could experience performance issues as a result of increasing network traffic between services. With increasing complexity, microservices might be more susceptible to mistakes and defects as well.
The modules in a modular monolithic architecture are created to function fluidly with one another, which improves performance and stability.
Cost-effective
Microservices need additional infrastructure for API gateways, load balancing, service discovery, etc. This makes it more expensive than modular monolithic architecture, especially in the early stages of development.
In a modular monolith, as all modules may be put on a single server, less infrastructure is required, making it significantly more cost-effective.
When to move to microservices?
When scalability is a critical factor
If the different modules need to scale differently, microservices may be a better choice. With microservices, it is easier to scale individual services independently, allowing for more granular control over the application.
When the team grows
If the development team is large, leading to increasing complexity, it is time to make the move. With microservices, each service can be developed and tested independently, allowing for better collaboration between teams.
When you need diverse technology
If there is a situation where different parts of the application require different technologies/languages, microservices is the way to go. With microservices, each service can use its own technology stack, allowing for greater flexibility in the development process.
When you have a firm grasp of the complex domain
When you have achieved a good understanding of the domain including its multiple sub-systems, microservices architecture can help encapsulate and manage the complexity. By breaking down the system into smaller, specialized services, the application will be easier to maintain and evolve over time.
In summary, it is beneficial to think of microservices as an end-goal rather than a starting point. A modular monolithic architecture in the early stages of software development would pave the way for microservices with a well-defined bounded context later. It will also help curb the inflated number of use case services.
So, before making the jump to microservices, we encourage leaders to consider the application's size and complexity, estimated user traffic and available skills resources. If microservices seems like a big leap, a modular monolith might just be the springboard you need.
Disclaimer: The statements and opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Thoughtworks.