0
0
Microservicessystem_design~7 mins

Anti-patterns (distributed monolith, chatty services) in Microservices - System Design Guide

Choose your learning style9 modes available
Problem Statement
When microservices are designed without clear boundaries or have excessive inter-service communication, the system behaves like a single monolith spread across multiple services. This causes slow responses, complex debugging, and tight coupling that defeats the purpose of microservices.
Solution
Avoid tight coupling by defining clear service boundaries and minimizing synchronous calls between services. Use asynchronous communication or event-driven patterns to reduce chatty interactions and ensure services can evolve independently.
Architecture
Service A
Service B
Service A
Event Bus

The first diagram shows services tightly coupled with many synchronous calls causing a distributed monolith. The second diagram shows services communicating asynchronously via an event bus to reduce chatty interactions.

Trade-offs
✓ Pros
Improves service independence and scalability by reducing tight coupling.
Reduces latency caused by multiple synchronous calls.
Simplifies debugging by limiting complex inter-service dependencies.
Enables teams to deploy and evolve services independently.
✗ Cons
Requires careful design of service boundaries and communication patterns.
Asynchronous communication can increase system complexity and eventual consistency challenges.
Refactoring existing distributed monoliths can be time-consuming and risky.
When building or refactoring microservices with high inter-service communication and tight coupling, especially at scale above hundreds of services or thousands of requests per second.
When the system is small with few services and low traffic, the overhead of complex asynchronous communication may outweigh benefits.
Real World Examples
Netflix
Refactored from a distributed monolith to event-driven microservices to reduce chatty synchronous calls and improve resilience.
Uber
Uses asynchronous messaging to decouple services and avoid chatty interactions in their ride matching and pricing systems.
Amazon
Avoids distributed monoliths by enforcing strict service boundaries and using event-driven architecture for order processing.
Code Example
The before code shows ServiceA calling ServiceB synchronously, creating tight coupling and chatty calls. The after code uses an EventBus to publish events asynchronously, allowing ServiceB to handle events without direct calls, reducing coupling and chatty communication.
Microservices
### Before: Chatty synchronous calls causing tight coupling
class ServiceA:
    def call_service_b(self):
        service_b = ServiceB()
        result = service_b.process()
        return result

class ServiceB:
    def process(self):
        # Some processing
        return "data"


### After: Asynchronous event-driven communication reducing chatty calls
import asyncio

class EventBus:
    def __init__(self):
        self.subscribers = {}

    def subscribe(self, event_type, handler):
        self.subscribers.setdefault(event_type, []).append(handler)

    async def publish(self, event_type, data):
        handlers = self.subscribers.get(event_type, [])
        for handler in handlers:
            await handler(data)

class ServiceA:
    def __init__(self, event_bus):
        self.event_bus = event_bus

    async def do_work(self):
        await self.event_bus.publish('event_b', {'info': 'start'})

class ServiceB:
    async def handle_event(self, data):
        # Process event asynchronously
        print(f"Processing event with data: {data}")

# Setup
bus = EventBus()
service_b = ServiceB()
bus.subscribe('event_b', service_b.handle_event)
service_a = ServiceA(bus)

# Run
asyncio.run(service_a.do_work())
OutputSuccess
Alternatives
API Gateway
Centralizes communication and reduces direct service-to-service calls by routing through a gateway.
Use when: When you want to simplify client interactions but still need to reduce chatty calls between backend services.
Backend for Frontend (BFF)
Creates specialized backend services per client type to reduce chatty calls from frontend to multiple microservices.
Use when: When frontend clients cause excessive calls to many services and need tailored aggregation.
Summary
Distributed monoliths occur when microservices are tightly coupled with excessive synchronous calls.
Reducing chatty services by using asynchronous communication improves scalability and independence.
Careful design of service boundaries and communication patterns prevents these anti-patterns.