Bird
Raised Fist0
Microservicessystem_design~7 mins

Why inter-service communication defines architecture in Microservices - Why This Architecture

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Problem Statement
When multiple services need to work together, poor communication design causes delays, data inconsistencies, and system failures. Without clear communication patterns, services become tightly coupled, making the system hard to scale, maintain, or evolve.
Solution
Designing how services talk to each other sets the foundation for the entire system. By choosing communication methods like synchronous calls or asynchronous messaging, the system controls data flow, fault tolerance, and scalability. This ensures services remain independent yet coordinated, improving reliability and flexibility.
Architecture
Service A
Service B
Message Bus

This diagram shows services communicating directly in a chain and also via an asynchronous message bus, illustrating different inter-service communication methods shaping the architecture.

Trade-offs
✓ Pros
Enables loose coupling, so services can evolve independently.
Improves fault isolation; one service failure doesn't cascade.
Supports scalability by choosing appropriate communication patterns.
Allows flexibility to mix synchronous and asynchronous calls.
✗ Cons
Increases complexity in managing communication protocols and data formats.
Requires careful handling of failures and retries to avoid data loss or duplication.
Can introduce latency if communication is not optimized.
Use when building distributed systems with multiple independent services requiring coordination, especially at scale above hundreds of requests per second.
Avoid complex inter-service communication in small, monolithic applications or systems with very low traffic where overhead outweighs benefits.
Real World Examples
Netflix
Uses asynchronous messaging and event-driven communication to decouple microservices, enabling high availability and independent deployment.
Uber
Employs synchronous REST calls combined with asynchronous messaging to balance real-time requests and background processing.
Amazon
Uses a mix of synchronous APIs and asynchronous queues to handle order processing and inventory updates reliably across services.
Code Example
The before code shows ServiceA calling ServiceB directly, creating tight coupling. The after code uses a MessageBus to publish and subscribe to events, enabling asynchronous communication and loose coupling between services.
Microservices
### Before: Tight coupling with direct synchronous calls
class ServiceA:
    def call_service_b(self):
        service_b = ServiceB()
        return service_b.process()

class ServiceB:
    def process(self):
        return "data"


### After: Loose coupling with asynchronous messaging
class MessageBus:
    def __init__(self):
        self.subscribers = {}

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

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


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

    def send_event(self):
        self.bus.publish('event_b', {'info': 'data'})


class ServiceB:
    def __init__(self, bus):
        bus.subscribe('event_b', self.handle_event)

    def handle_event(self, data):
        print(f"Processing {data}")
OutputSuccess
Alternatives
Monolithic Architecture
All components run within a single process without network communication between services.
Use when: Choose when the system is small, simple, and requires minimal scaling.
Service Mesh
Adds a dedicated infrastructure layer to manage service-to-service communication transparently.
Use when: Choose when you need advanced features like observability, security, and traffic control at scale.
Summary
Inter-service communication design prevents bottlenecks and failures in distributed systems.
Choosing communication patterns shapes system scalability, reliability, and flexibility.
Real-world systems mix synchronous and asynchronous methods to balance performance and decoupling.

Practice

(1/5)
1. Which of the following best explains why inter-service communication is crucial in microservices architecture?
easy
A. It only affects the user interface design of the application.
B. It determines how services coordinate and impacts system performance and reliability.
C. It is used to store data permanently in the database.
D. It defines the programming language used for each service.

Solution

  1. Step 1: Understand the role of inter-service communication

    Inter-service communication allows different microservices to work together by exchanging data and requests.
  2. Step 2: Identify its impact on system qualities

    This communication affects how fast and reliable the overall system is, as services depend on each other to complete tasks.
  3. Final Answer:

    It determines how services coordinate and impacts system performance and reliability. -> Option B
  4. Quick Check:

    Communication defines coordination and performance = B [OK]
Hint: Focus on coordination and system impact for communication [OK]
Common Mistakes:
  • Confusing communication with UI design
  • Thinking communication stores data permanently
  • Believing communication defines programming language
2. Which syntax correctly represents asynchronous communication between two microservices using message queues?
easy
A. serviceA.publishToQueue('taskQueue', message)
B. serviceA.sendRequest(serviceB)
C. serviceA.call(serviceB).wait()
D. serviceA.invoke(serviceB).sync()

Solution

  1. Step 1: Identify asynchronous communication syntax

    Asynchronous communication uses message queues where a service publishes messages without waiting for immediate response.
  2. Step 2: Match syntax to asynchronous pattern

    publishToQueue sends a message to a queue, fitting asynchronous style; other options imply direct or synchronous calls.
  3. Final Answer:

    <code>serviceA.publishToQueue('taskQueue', message)</code> -> Option A
  4. Quick Check:

    Message queue publish = A [OK]
Hint: Look for 'publish' or 'queue' to spot async communication [OK]
Common Mistakes:
  • Choosing direct method calls as async
  • Confusing synchronous wait with async
  • Ignoring message queue terminology
3. Given the following code snippet for synchronous communication, what will be the output if serviceB.process() takes 3 seconds to respond?
response = serviceA.call(serviceB.process)
print('Response received')
medium
A. Response received (printed immediately)
B. Response received printed twice
C. No output due to error
D. Response received (printed after 3 seconds)

Solution

  1. Step 1: Understand synchronous call behavior

    Synchronous calls wait for the called service to finish before continuing execution.
  2. Step 2: Analyze the code flow

    Since serviceB.process() takes 3 seconds, print waits and executes after the response arrives.
  3. Final Answer:

    Response received (printed after 3 seconds) -> Option D
  4. Quick Check:

    Synchronous call delays output = D [OK]
Hint: Synchronous means wait before next step [OK]
Common Mistakes:
  • Assuming immediate print without wait
  • Thinking output prints twice
  • Confusing synchronous with asynchronous
4. Identify the error in this asynchronous communication example using a message queue:
serviceA.publish('taskQueue', message)
serviceB.process()
serviceB.consume('taskQueue')
medium
A. serviceB.consume should be called before process to receive messages
B. serviceA.publish should wait for serviceB.process to finish
C. serviceB.process() should be called after consume
D. No error; the code is correct

Solution

  1. Step 1: Understand message consumption order

    To process messages, the consumer must subscribe or consume from the queue before processing.
  2. Step 2: Identify incorrect sequence

    Calling serviceB.process() before consume means no messages are received yet, causing a logic error.
  3. Final Answer:

    serviceB.consume should be called before process to receive messages -> Option A
  4. Quick Check:

    Consume before processing = C [OK]
Hint: Consume messages before processing them [OK]
Common Mistakes:
  • Calling process before consuming messages
  • Expecting publish to wait for processing
  • Thinking code order does not matter
5. You are designing a microservices system where Service A must send a request to Service B and continue working without waiting for a response. Which communication pattern should you choose to ensure scalability and loose coupling?
hard
A. Direct database polling by Service A
B. Synchronous HTTP request with retries
C. Asynchronous messaging via a message queue
D. Tightly coupled RPC calls with blocking

Solution

  1. Step 1: Analyze requirement for non-blocking communication

    Service A must not wait for Service B's response, so asynchronous communication is needed.
  2. Step 2: Choose scalable and loosely coupled pattern

    Using a message queue allows Service A to send messages and continue, while Service B processes independently, supporting scalability and loose coupling.
  3. Final Answer:

    Asynchronous messaging via a message queue -> Option C
  4. Quick Check:

    Async messaging for non-blocking and scalability = A [OK]
Hint: Pick async messaging for non-blocking, scalable design [OK]
Common Mistakes:
  • Choosing synchronous calls causing blocking
  • Using direct DB polling which is inefficient
  • Selecting tightly coupled RPC reducing flexibility