Preface
When designing software system and/or architecture, terms like microservices, cloud-native, and serverless grab headlines for their scalability, resilience, and ease of development. However, there's a less glamorous, yet increasingly common architecture pattern that many developers encounter: the distributed monolith. This blog post will dive into what a distributed monolith is, why they form, the problems they present, and how to address these issues, with a focus on companies like Shopify, which have navigated these waters.
What is a Distributed Monolith?
A distributed monolith isn't an architecture style you aim for; it's more of an anti-pattern that evolves unintentionally. Here's how we can define it:
- Tight Coupling Across Services: While services are deployed independently, they share logic, databases, or configurations in a way that makes them highly interdependent.
- Centralized Deployment Logic: Despite being deployed across multiple nodes or containers, the services still require synchronized updates because they rely on each other for functionality.
- Shared Data Sources: Multiple services access the same database, leading to complex data management issues.
Types of Monoliths
Traditional Monoliths
Everything is bundled togetherโuser interface, business logic, and data layersโinto a single deployment. There are no clear boundaries between features, and shared libraries are common. A single deployment unit encompassing all layers of the application (GUI, business logic, data access) connected to one database.
Modular Monoliths
The system is divided into distinct modules with well-defined responsibilities. While the modules are independent, they share a single deployment unit and database. A single deployment unit with distinct modules (Module A, B, C, etc.) that can be developed independently but are still part of a unified codebase, all sharing one database.
Distributed Monoliths
This is a hybrid of microservices and monoliths, often created unintentionally. Services are deployed like microservices but still rely on monolith principles, with tight coupling, shared databases, chatty communication, and limited independent scalability. Multiple services or modules deployed separately, each potentially with its own database, but still highly interdependent, forming a distributed system that behaves like a monolith due to tight coupling.
Why Do Distributed Monoliths Form?
- Evolution from Monoliths: Often, companies start with a monolithic architecture for simplicity. As the application grows, attempts to modularize it into microservices can lead to a distributed monolith if not done correctly.
- Lack of Architectural Discipline: Without clear boundaries or strict adherence to microservices principles, services can become too interconnected.
- Legacy System Integration: Integrating new services with older systems can inadvertently create a distributed monolith if the old systems' monolithic nature is not adequately decoupled.
Problems with Distributed Monoliths
- Maintenance Complexity: A change in one service can ripple through the system, requiring updates to several other services, thus complicating maintenance and development.
- Scalability Issues: While services can scale independently, the tight coupling means scaling one service often requires scaling others, reducing the scalability benefits of distributed systems.
- Reduced Fault Tolerance: If one service crashes, others might fail too because they depend on each other for functionality.
How to Address the Distributed Monolith Issue
- Re-evaluate Architecture: Revisit current architecture. Identify services that are too tightly coupled and see if they can be logically split or if shared components can be externalized.
-
Service Decomposition:
Gradually decompose services. Start by identifying services that can truly stand alone and refactor them to reduce dependencies.
- Database Per Service: Aim for each service to have its own database, reducing contention and improving data management.
- API Gateway and Event-Driven Architecture: By implementing an API Gateway to manage service interactions and consider moving towards an event-driven model where services communicate through events rather than direct calls.
- Testing and Monitoring: Rigorous testing for each service in isolation and monitoring to understand service interactions and dependencies can help in slowly untangling a distributed monolith.
- Documentation and Understanding: Documenting the current state of service dependencies. Understanding how services interact can guide refactoring efforts.
- Education and Culture: Sometimes, a distributed monolith is a result of cultural practices. Educate teams about microservice principles, promote ownership, and encourage a culture of autonomy.
Companies Approach to Distributed Monoliths
Shopify
The best example of this is Shopify , with over 3 million lines of code one of the largest monoliths in the world. Instead of rewriting its entire monolith as microservices, Shopify chose modularization as the solution. Also, monoliths can scale; they served ๐ญ.๐ฎ๐ณ ๐บ๐ถ๐น๐น๐ถ๐ผ๐ป ๐ฟ๐ฒ๐พ๐๐ฒ๐๐๐ ๐ฝ๐ฒ๐ฟ ๐๐ฒ๐ฐ๐ผ๐ป๐ฑ ๐ฑ๐๐ฟ๐ถ๐ป๐ด ๐๐น๐ฎ๐ฐ๐ธ ๐๐ฟ๐ถ๐ฑ๐ฎ๐. Despite the complexity, Shopify has managed to design a system where the monolith is internally split into components focused on different business domains, allowing for a form of modularity within the monolith. This approach, termed by Shopify as a "Majestic Monolith," helps maintain system coherence while allowing for internal modularity.
Agoda
While Agoda has been transitioning from a monolithic GraphQL API to a microservices architecture, their journey highlights the challenges and strategies of dealing with distributed monolith characteristics. They've employed a client-first strategy, ensuring that client applications can handle both the monolith and emerging microservices in parallel, gradually decoupling services without immediate disruption to the users.
Conclusion
Distributed monoliths, while a misstep in software architecture, highlight the complexity of moving from monoliths to microservices. Companies like Shopify and Agoda demonstrate the importance of strategic service decomposition for scalability. Awareness of service interdependencies and proactive architectural discipline can transform these challenges into opportunities for robust system evolution.
NOTE: I'm constantly delighted to receive feedback. Whether you spot an error, have a suggestion for improvement, or just want to share your thoughts, please don't hesitate to comment/reach out. I truly value connecting with readers!