Verified by Garnet Grid

Microservices Communication Patterns

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

How microservices talk to each other determines whether your architecture is resilient or fragile. Synchronous HTTP calls create tight coupling — if one service is slow, everything is slow. Asynchronous messaging decouples services but adds complexity around ordering, idempotency, and eventual consistency. The right choice depends on the use case.


Sync vs Async

FactorSynchronous (HTTP/gRPC)Asynchronous (Messages/Events)
CouplingTight — caller waits for responseLoose — fire and forget
LatencyAdds up serially (A → B → C → D)Parallel processing
Failure handlingCascading failuresIsolated failures
Data consistencyImmediate (request-response)Eventual consistency
ComplexityLowerHigher (queues, ordering, idempotency)
Best forQueries, reads, simple CRUDEvents, commands, long operations

Communication Decision Tree

Does the caller need an immediate response?
├── Yes → Is latency critical (< 50ms)?
│   ├── Yes → gRPC (binary, fast)
│   └── No  → REST/HTTP (simpler)
└── No  → Is ordering important?
    ├── Yes → Message Queue (Kafka, SQS)
    │         with partition key for ordering
    └── No  → Event Bus (SNS, EventBridge)
              for fan-out to multiple consumers

API Gateway

                       ┌───────────────┐
  Client ────────────▶ │  API Gateway  │
                       │               │
                       │ • Auth        │
                       │ • Rate limit  │
                       │ • Routing     │
                       │ • Aggregation │
                       └───┬───┬───┬───┘
                           │   │   │
                    ┌──────┘   │   └──────┐
                    ▼          ▼          ▼
              ┌──────┐  ┌──────┐  ┌──────┐
              │User  │  │Order │  │Pay   │
              │Svc   │  │Svc   │  │Svc   │
              └──────┘  └──────┘  └──────┘

Message Queue Patterns

Point-to-Point (Command)

Producer ──── message ────▶ [Queue] ────▶ Consumer
                                         (one consumer
                                          processes each
                                          message)

Pub/Sub (Event)

Producer ──── event ────▶ [Topic] ──┬──▶ Consumer A
                                    ├──▶ Consumer B
                                    └──▶ Consumer C
                                    (all consumers
                                     receive every event)

Idempotent Consumer

async def handle_order_created(event):
    """Process order event idempotently."""
    event_id = event["event_id"]
    
    # Check if already processed
    if await db.exists("processed_events", event_id):
        logger.info(f"Event {event_id} already processed, skipping")
        return
    
    # Process the event
    await create_invoice(event["order_id"])
    
    # Mark as processed (idempotency key)
    await db.insert("processed_events", {
        "event_id": event_id,
        "processed_at": datetime.utcnow()
    })

Anti-Patterns

Anti-PatternProblemFix
Synchronous chain (A→B→C→D)Latency = sum of all services, any failure breaks chainAsync where possible, parallelize sync calls
Distributed monolithMicroservices that must deploy togetherDecouple with events, contract testing
No idempotencyRetry causes duplicate processingIdempotency keys on all consumers
Direct service-to-service HTTPTight coupling, service discovery complexityAPI gateway or service mesh
No dead letter queueFailed messages lost foreverDLQ + monitoring + alerting

Checklist

  • Communication pattern chosen per interaction (sync vs async)
  • API gateway for external traffic (auth, rate limiting, routing)
  • Service discovery: DNS-based or service mesh
  • Async messaging for events and commands (Kafka, SQS, RabbitMQ)
  • Idempotent consumers on all message handlers
  • Dead letter queue configured with monitoring
  • Circuit breakers on all synchronous calls
  • Timeout and retry policies per service call
  • Contract tests between services

:::note[Source] This guide is derived from operational intelligence at Garnet Grid Consulting. For microservices consulting, visit garnetgrid.com. :::

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →