Question 15 · Section 17

How to organize communication between microservices

Microservices communicate in two main ways:

Language versions: English Russian Ukrainian

🟢 Junior Level

Microservices communicate in two main ways:

1. Synchronous (HTTP/gRPC):

Service A → HTTP request → Service B → HTTP response → Service A

2. Asynchronous (Kafka/RabbitMQ):

Service A → message to Queue → Service B picks it up when ready

🟡 Middle Level

Synchronous communication

REST:

@RestController
public class OrderController {
    private final UserServiceClient userService;

    @GetMapping("/orders/{id}")
    public Order getOrder(@PathVariable Long id) {
        Order order = orderRepository.findById(id);
        User user = userService.getUser(order.userId());
        return new Order(order, user);
    }
}

gRPC:

service UserService {
    rpc GetUser (UserRequest) returns (UserResponse);
}

Asynchronous communication

Kafka:

@KafkaListener(topics = "user-events")
public void onUserEvent(UserEvent event) {
    if (event.type() == USER_CREATED) {
        userRepository.save(event.toUser());
    }
}

Common mistakes

  1. Cascading failures:
    Service A → B → C → D
    D is down → C is down → B is down → A is down
    Solution: Circuit Breaker, Timeout
    

🔴 Senior Level

Architectural Trade-offs

When NOT to use synchronous communication

  • Long call chains (A→B→C→D) — cascade failure risk
  • Background tasks (sending email, generating reports)
  • High load — asynchronous better buffers traffic spikes
Synchronous Asynchronous
Simpler: one HTTP call → response, no broker/serialization overhead. More fault-tolerant
Faster for simple cases: no broker/serialization overhead. Better for scaling
Temporal coupling Decoupled
Cascading failures Eventual consistency

Production Experience

API Composition:

// thenCombine — combines two futures, waiting for both.
// join() — blocks until result (like get(), but without checked exception).
// If one future fails — the entire CompletableFuture pipeline fails.
@GetMapping("/dashboard/{userId}")
public CompletableFuture<Dashboard> getDashboard(@PathVariable Long userId) {
    CompletableFuture<User> user = userService.getUserAsync(userId);
    CompletableFuture<List<Order>> orders = orderService.getOrdersAsync(userId);
    CompletableFuture<List<Notification>> notifications =
        notificationService.getNotificationsAsync(userId);

    return user.thenCombine(orders, (u, o) ->
        new Dashboard(u, o, notifications.join()));
}

Best Practices

✅ Async for events
✅ Sync for data requests
✅ Circuit Breaker for sync calls
✅ Idempotency for async messages

❌ Long call chains
❌ Without timeout and retry
❌ Without error handling

🎯 Interview Cheat Sheet

Must know:

  • Two approaches: synchronous (HTTP/gRPC) and asynchronous (Kafka/RabbitMQ)
  • Synchronous: simpler, instant response, but cascade failure risk
  • Asynchronous: more fault-tolerant, scalable, but eventual consistency
  • API composition: combining results from multiple services (CompletableFuture.thenCombine)
  • Circuit Breaker is mandatory for sync calls
  • Idempotency is mandatory for async messages
  • Do NOT use synchronous for long chains (A→B→C→D), background tasks, high load

Frequent follow-up questions:

  • REST vs gRPC? REST is simpler, gRPC is faster (protobuf), stricter contract.
  • Kafka vs RabbitMQ? Kafka — persistent log, replay, higher throughput. RabbitMQ — flexible routing, easier setup.
  • How to avoid cascade failure? Circuit Breaker, Timeout, Retry — for sync; async queue — for background.
  • What is API composition? Querying multiple services in parallel, combining results (thenCombine).

Red flags (NOT to say):

  • “Synchronous is always better, it’s simpler” — no, cascade failure risk
  • “Asynchronous for everything” — no, harder to debug, eventual consistency
  • “Chain A→B→C→D→E is normal practice” — no, cascade failure is guaranteed
  • “Idempotency is not needed for Kafka” — it is needed, duplicate delivery is possible

Related topics:

  • [[16. What is the difference between synchronous and asynchronous communication]]
  • [[5. What is Circuit Breaker pattern]]
  • [[19. What is Retry pattern and how to use it correctly]]
  • [[9. What is API Gateway and what problems does it solve]]
  • [[2. What is the difference between choreography and orchestration in Saga]]