What is Distributed Monolith?

6 min read

Source: What is Distributed Monolith?
1. Introduction: Understanding the Distributed Monolith
A Distributed Monolith refers to a system that appears to follow a distributed architecture, like microservices, but is actually tightly coupled and interdependent. While it may consist of multiple services, these services are often so dependent on each other that they behave as a single, cohesive unit. As a result, it inherits many of the limitations of a monolithic system, such as inflexibility and difficulty scaling parts independently.
2. Characteristics of a Distributed Monolith

Inflexible Dependencies
One of the main indicators of a Distributed Monolith is tightly coupled services. When services are heavily dependent on each other, changes to one service frequently require changes to other services. This dependency chain makes it difficult to deploy services independently and hinders the scalability of individual components. For example, a front-end service may depend on a back-end service for user authentication and data retrieval. Changes in the data structure on the back-end side could force changes on the front-end as well.
Synchronous Communication
Distributed Monoliths often rely on synchronous, RESTful communication, where services directly call each other to complete a request. This creates a fragile system in which a failure or delay in one service can cascade through the entire application, reducing its overall resilience. This dependency on synchronous calls can also lead to latency issues, as each service must wait for responses from others before proceeding.
Code Example: Here is a sample Java service that demonstrates this synchronous dependency:
public class OrderService {
public String getOrderDetails(String orderId) {
String customerDetails = restTemplate.getForObject("http://customer-service/customer/" + orderId, String.class);
String productDetails = restTemplate.getForObject("http://product-service/product/" + orderId, String.class);
return "Order Details: " + customerDetails + " " + productDetails;
}
}
In this example, OrderService relies on both customer-service and product-service to fetch details. If either service is down, OrderService will also fail.
Shared Database
Often, services in a Distributed Monolith share a single database. This prevents services from operating independently and creates a high level of coupling at the data layer. For example, if one service needs to alter a table, it may require all services using that table to be updated accordingly, making deployments more complex and error-prone.
3. Causes of a Distributed Monolith
Partial Decomposition
Many Distributed Monoliths arise when organizations attempt to decompose a monolithic application but fail to separate the services adequately. This might happen when business logic is spread across multiple services without clear boundaries. Instead of independent services, you end up with microservices that still share much of the original monolithic structure and dependencies.
Overemphasis on RESTful Communication
Many developers default to RESTful APIs for service-to-service communication. While REST is a robust protocol, its synchronous nature can enforce tight coupling. A service relying on REST calls to other services cannot proceed until it receives a response, creating dependencies that contradict the autonomous nature of microservices.
Shared Database Architecture
If services are not given their own data stores, they remain interlinked through a shared database. This often results from an attempt to maintain a single source of truth, but it compromises the independence of each service. Changes in one service's data requirements affect other services, creating a cascade of changes that must be managed across the application.
4. Impact of a Distributed Monolith on the System
Limited Scalability
A Distributed Monolith limits the scalability of individual components, as services are not genuinely independent. For example, if a high-traffic service is coupled with several low-traffic services, scaling the high-traffic service may inadvertently require scaling the entire suite, increasing costs and complexity.
Reduced Reliability and Resilience
When services depend on one another for essential operations, a failure in one service can lead to system-wide outages. This is because each service must wait for the others to respond before completing its operations. Such dependencies reduce the overall resilience of the system, leading to cascading failures.
Code Example: Here is a demonstration of how a cascading failure might occur:
try {
String productInfo = restTemplate.getForObject("http://product-service/api", String.class);
} catch (Exception e) {
throw new RuntimeException("Product service unavailable", e);
}
If the product-service fails, it triggers an exception in every service that depends on it, causing a chain reaction of failures.
Increased Maintenance and Deployment Costs
Coordinated deployments are necessary in a Distributed Monolith because changes often impact multiple services. For example, a change in one service’s data model might require simultaneous updates to all services that interact with the same data, making independent deployments nearly impossible.
5. Ways to Mitigate and Reduce Distributed Monolith
Implement Asynchronous Communication Patterns
Using message brokers like Kafka or RabbitMQ, you can decouple services by transitioning to event-driven architectures. This approach allows services to publish and consume messages asynchronously, reducing dependencies on synchronous calls and enhancing system resilience.
Code Example: An example of asynchronous communication using Kafka:
@Service
public class OrderService {
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void placeOrder(Order order) {
kafkaTemplate.send("order-topic", order.toString());
}
}
Decouple the Database
By giving each service its own database, you ensure data sovereignty, allowing services to operate independently. This not only reduces coupling but also enables each service to scale individually according to its load requirements.
Adopt Domain-Driven Design (DDD)
DDD helps establish clear boundaries between services by grouping related business logic. Services that are responsible for distinct domains have more focused responsibilities, making them more independent and resilient.
6. Steps to Transition from Distributed Monolith to Microservices
Identify Service Boundaries
Analyze your application to identify distinct business domains that can function independently. Define boundaries around these domains to create truly autonomous services.
Refactor for Asynchronous Communication
Move away from synchronous API calls between services, leveraging message brokers to handle communication asynchronously. This transition will reduce the direct dependencies that contribute to a Distributed Monolith.
Decouple the Database
Gradually migrate shared databases into individual stores for each service. You might start with separate schemas and then transition to fully independent databases.
Implement CI/CD Pipelines for Independent Deployments
Set up Continuous Integration and Continuous Deployment (CI/CD) pipelines for each microservice, enabling them to be developed, tested, and deployed independently. This setup allows each service to evolve without impacting others.
7. Conclusion
Moving away from a Distributed Monolith to a truly distributed microservices architecture requires effort, but it pays off in scalability, resilience, and maintainability. By decoupling services and adopting asynchronous communication, you can reduce dependencies and transform your system into one that is truly modular. If you have questions about identifying or transitioning from a Distributed Monolith, please leave a comment below!
Read more at : What is Distributed Monolith?
0
Subscribe to my newsletter
Read articles from Tuanhdotnet directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Tuanhdotnet
Tuanhdotnet
I am Tuanh.net. As of 2024, I have accumulated 8 years of experience in backend programming. I am delighted to connect and share my knowledge with everyone.