Microservices Communication Patterns

Design inter-service communication. Covers synchronous vs asynchronous, API gateways, service discovery, message queues, event buses, and handling distributed failures.

Microservices communication patterns define how services exchange data and coordinate behavior across a distributed system. They matter when services must scale independently, evolve at different paces, and tolerate partial failures. These patterns govern how a service discovers others, sends messages, handles retries, and responds to errors—decisions that shape reliability, latency, and operational complexity.

Synchronous Communication via HTTP

When a service must receive a response within a bounded time, use HTTP-based synchronous calls. The caller blocks until the callee returns a result, ideal for request-response workflows like user-facing APIs, transactional operations, or real-time dashboards.

The most common pattern is REST over HTTP/1.1 or HTTP/2, with JSON payloads and standard status codes. Clients use curl to test endpoints:

curl -X POST https://payment-service.example.com/api/v1/pay \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJm3" \
  -d '{"amount": 99.95, "currency": "USD", "orderId": "ORD-2023-1001"}'

The server responds with a JSON body and status code:

{
  "transactionId": "TXN-2023-08765",
  "status": "confirmed",
  "timestamp": "2023-10-05T12:34:56Z"
}

Failure responses follow standard conventions: 400 Bad Request for invalid input, 404 Not Found for missing resources, 429 Too Many Requests for rate-limiting, and 503 Service Unavailable for transient errors.

Use HTTP/2 for high-throughput systems with many concurrent requests. Enable push and multiplexing to reduce latency. A service may set Accept: application/json and Accept-Encoding: gzip to optimize bandwidth.

Key Challenges

  GET /orders/123?timeout=1000
  POST /orders
  Idempotency-Key: 7a3b8c1d-1234-5678-9abc-def123456789

The server stores the key and returns 201 Created on first call, and 200 OK on subsequent identical calls.

Asynchronous Communication via Message Queues

When services need decoupling, scalability, and fault tolerance, use message queues. A producer publishes messages to a queue, and one or more consumers process them asynchronously.

Common implementations include RabbitMQ, Apache Kafka, and Amazon SQS. All use a topic-based or queue-based model with durable, persistent messages.

A service publishes a message using curl to a RESTful broker:

curl -X POST https://message-broker.example.com/queues/orders \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "ORD-2023-1002",
    "customerId": "CUST-100",
    "items": [
      { "productId": "PROD-101", "quantity": 2 }
    ],
    "timestamp": "2023-10-05T12:35:00Z"
  }'

Consumers poll the queue using a GET /queues/orders?wait=30 endpoint, or subscribe via long-lived HTTP streams.

Message Structure and Delivery Guarantees

Messages are structured as JSON or Avro, with metadata and payload:

{
  "messageId": "MSG-2023-08766",
  "topic": "orders.created",
  "timestamp": "2023-10-05T12:35:00Z",
  "headers": {
    "correlationId": "CORR-2023-001",
    "contentType": "application/json"
  },
  "payload": {
    "orderId": "ORD-2023-1002",
    "customerId": "CUST-100",
    "items": [
      { "productId": "PROD-101", "quantity": 2 }
    ]
  }
}

Two key guarantees underpin reliability:

  POST /queues/orders/ack
  {
    "messageId": "MSG-2023-08766",
    "success": true,
    "workerId": "worker-7"
  }

If the consumer fails before ACK, the message is redelivered.

Common Failure Modes

  {
    "originalMessageId": "MSG-2023-08766",
    "error": "Invalid JSON",
    "retries": 3,
    "failedAt": "2023-10-05T12:37:00Z",
    "payload": "{ \"orderId\": \"ORD-2023-1002\", \"items\": [ { \"productId\": \"PROD-101\" } ] }"
  }

Event-Driven Communication via Event Buses

When services react to domain state changes, use event-driven communication. A service emits an event when a significant change occurs, and other services subscribe to it.

Events are named using PascalCase or kebab-case and are published to a topic or stream. Events are immutable, self-contained, and carry metadata.

Example event:

{
  "eventType": "OrderConfirmed",
  "eventId": "EVT-2023-08767",
  "timestamp": "2023-10-05T12:36:00Z",
  "source": "orders-service",
  "data": {
    "orderId": "ORD-2023-1002",
    "status": "confirmed",
    "confirmedAt": "2023-10-05T12:36:00Z"
  }
}

Consumers subscribe to topics using a subscribe endpoint:

POST /events/subscriptions
{
  "topic": "orders.confirmed",
  "url": "https://inventory-service.example.com/events/handle",
  "format": "json",
  "retryStrategy": {
    "maxRetries": 5,
    "backoff": "exponential",
    "initialDelay": 1000,
    "multiplier": 2
  }
}

The event bus pushes events to subscribers via HTTP or WebSocket.

Handling Event Processing

  {
    "eventType": "OrderConfirmed",
    "eventVersion": "1.1",
    "timestamp": "2023-10-05T12:36:00Z",
    "data": {
      "orderId": "ORD-2023-1002",
      "status": "confirmed",
      "confirmedAt": "2023-10-05T12:36:00Z",
      "totalAmount": 199.9
    }
  }

Failure Modes

Service Discovery and Dynamic Configuration

When services must locate each other dynamically, use service discovery. A service registers itself with a discovery service, and others query it to find endpoints.

Services register using POST /services:

{
  "serviceName": "payment-service",
  "host": "10.0.1.10",
  "port": 8080,
  "protocol": "http",
  "tags": ["payment", "online", "v1"],
  "healthCheck": {
    "endpoint": "/health",
    "interval": 10,
    "timeout": 5
  }
}

Clients query the discovery service using GET /services/payment-service to retrieve the current list of instances.

Use DNS for simple discovery: payment-service.discovery.example.com resolves to a list of IPs.

For dynamic configuration, services load settings from a central store. A service reads its configuration at startup:

{
  "database": {
    "url": "postgresql://db.example.com:5432/orderdb",
    "poolSize": 20
  },
  "payment": {
    "gateway": "stripe",
    "apiKey": "sk_live_1234567890",
    "timeout": 3000
  },
  "tracing": {
    "enabled": true,
    "collector": "http://jaeger-collector.example.com:14268/api/traces"
  }
}

The configuration is stored in a file, database, or key-value store. Services watch for changes and reload configuration without restart.

Key Issues

  GET /config/watch?service=payment-service

The server responds with 200 OK and streams updates in text/event-stream format.

Error Handling and Retries

When communication fails, services must recover gracefully.

Use HTTP status codes to communicate errors:

For retries, implement exponential backoff with jitter. A client retries a failed request with increasing delays:

{
  "retries": [
    { "attempt": 1, "delay": 100, "backoff": "exponential" },
    { "attempt": 2, "delay": 200, "backoff": "exponential" },
    { "attempt": 3, "delay": 400, "backoff": "exponential" }
  ]
}

Use Retry-After header to communicate the expected delay:

HTTP/1.1 503 Service Unavailable
Retry-After: 60
Content-Type: application/json

{
  "error": "Service temporarily overloaded",
  "suggestedWait": 60
}

Idempotency and Consistency

When a service calls another and fails, the caller must decide whether to retry or consider the call a failure. Use correlationId and messageId to trace requests across services.

A service may use transactional outbox to ensure that a domain event is published only after the transaction commits. The outbox table stores events with metadata, and a background process sends them to the event bus.

In summary, microservices communication patterns are not a set of tools but a set of decisions: when to use HTTP, when to use messages, how to handle failures, and how to ensure data consistency. Each choice shapes the behavior, reliability, and maintainability of the entire system.

This page was rewritten on 15 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.