How do microservices communicate with each other?
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
Microservices communicate synchronously when the caller needs an immediate result and asynchronously when work can be accepted and completed through messaging. HTTP and gRPC are common request/response choices; queues, topics, and event streams support asynchronous delivery.
For example, an order service may call inventory synchronously to confirm a reservation, then publish OrderPlaced so fulfillment can continue independently. The HTTP call adds availability and latency coupling: a slow inventory service delays the order path. Messaging can buffer work, but the consumer must handle duplicates, retries, poison messages, ordering, schema evolution, and eventual consistency.
Asynchronous does not automatically mean resilient, and synchronous does not automatically mean tightly coupled in every dimension. I choose from whether the caller needs an answer now, expected throughput, failure/retry semantics, ordering, consistency, and observability. Timeouts and idempotency matter in both designs.
How do microservices communicate with each other
Direct Answer
Microservices use synchronous request/response protocols such as HTTP or gRPC and asynchronous messaging such as queues or event streams. Synchronous calls create temporal and availability coupling but give an immediate result. Asynchronous communication can buffer work and decouple timing, yet delivery semantics, ordering, retries, idempotency, and observability must be designed.
Code Example
// Synchronous: call another service and WAIT for the response (REST)
@Service
class OrderService {
private final WebClient payment;
public PaymentResult charge(Order order) {
return payment.post().uri("/charge")
.bodyValue(order).retrieve()
.bodyToMono(PaymentResult.class)
.block(); // blocks until PaymentService answers
}
}
// Asynchronous: publish an event and move on (Kafka) — consumers react later
@Service
class OrderEvents {
private final KafkaTemplate<String, OrderPlaced> kafka;
public void publish(Order order) {
kafka.send("orders", new OrderPlaced(order.id())); // fire and forget
}
}
Common Mistakes & Pitfalls
- Overusing synchronous calls: Freshers often chain REST calls like OrderService calls InventoryService, which calls WarehouseService, which calls ShippingService. Each hop adds latency and multiplies failure risk—if any service in the chain is slow or down, the entire request fails. This creates a brittle system.
- Not handling timeouts: Making a REST call without setting a timeout means if the other service hangs, your thread blocks forever. In production, you need circuit breakers like Resilience4j to fail fast when a service is unhealthy.
- Ignoring idempotency in async: With message brokers, the same message might be delivered twice (at-least-once delivery). If your code doesn't check for duplicates, you might charge a customer twice or send the same email repeatedly. Freshers forget to design handlers to be idempotent.
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.