Call
Microservices

Microservice Communication Patterns: REST, gRPC, Queues, Pub/Sub, Streaming, and Workflows

A practical decision guide to microservice communication: synchronous REST/gRPC, asynchronous queues, pub/sub, event streaming, callbacks, bidirectional streams, choreography, orchestration, and failure handling.

Published: July 14, 2025
Updated: September 10, 2026
11 min read
Microservice Communication Patterns: REST, gRPC, Queues, Pub/Sub, Streaming, and Workflows

Microservice Communication Patterns: REST, gRPC, Queues, Pub/Sub, Streaming, and Workflows

Microservices communicate over unreliable networks.

That single fact changes architecture dramatically.

A local function call either returns or throws. A remote call can succeed on the server while the client times out, arrive twice after retries, wait behind a queue, be delivered out of order, or depend on another service that is temporarily unavailable.

Choosing how services communicate is therefore not just a protocol decision. It determines latency, coupling, consistency, failure behavior, scaling, and observability.

This guide compares the patterns that actually matter in production.


Start With the First Decision: Synchronous or Asynchronous?

AWS Prescriptive Guidance groups microservice communication into two broad styles.

Synchronous

The caller sends a request and waits for the result.

Service A → Service B
          ← response

Typical technologies:

  • HTTP/REST
  • gRPC unary RPC
  • GraphQL between selected boundaries

Use this when the caller cannot continue without an immediate answer.

Asynchronous

The caller submits work or publishes information and continues without waiting for the final result.

Service A → Queue / Broker → Service B
Service A continues

Typical technologies:

  • queues
  • pub/sub
  • event streaming
  • callbacks
  • workflow engines

Use this when delayed processing is acceptable or when you need to absorb load and reduce availability coupling.

Neither style is universally better.


Pattern 1: REST Request-Response

REST over HTTP remains a strong default for externally exposed APIs and simple synchronous service-to-service communication.

Example:

GET /customers/42

The caller needs customer data before it can continue.

Good fit

  • CRUD-style APIs
  • public APIs
  • simple request-response operations
  • broad client compatibility

Advantages

  • familiar tooling
  • easy debugging
  • easy proxy/gateway support
  • human-readable payloads when using JSON

Risks

  • synchronous dependency on downstream availability
  • serialization/network overhead
  • long dependency chains amplify latency

Use REST because the interaction is naturally request-response, not because every service needs an HTTP endpoint for every internal event.


Pattern 2: gRPC Unary Calls

gRPC uses service contracts commonly defined with Protocol Buffers and is often attractive for internal RPC.

Conceptually:

rpc GetInventory(GetInventoryRequest)
    returns (GetInventoryResponse);

Good fit

  • internal service-to-service APIs
  • strongly typed contracts
  • low-latency/high-throughput RPC
  • polyglot systems with generated clients

Advantages

  • compact binary serialization
  • generated clients/servers
  • explicit service contracts
  • deadlines and streaming support

Risks

  • still synchronous in unary mode
  • dependency failures still propagate
  • browser/public compatibility may need additional infrastructure

REST vs gRPC is not “slow vs fast.” The more important question is whether you need a public resource-oriented API or a strongly typed internal RPC contract.


Pattern 3: Server Streaming

Sometimes the client sends one request and the service produces multiple responses over time.

Client → request
Server → update 1
       → update 2
       → update 3

gRPC supports server-streaming RPCs, and similar behavior can be implemented with SSE or WebSockets depending on the client environment.

Good fit

  • incremental results
  • status/progress streams
  • AI token streaming
  • live server updates

The connection itself becomes a resource, so plan for cancellation, timeouts, backpressure, and reconnect behavior.


Pattern 4: Bidirectional Streaming

In bidirectional streaming, both client and server can independently send multiple messages over one logical call.

gRPC's official model preserves message order within each side's stream while allowing both sides to read and write independently.

Client ⇄ Server

Good fit

  • interactive realtime sessions
  • collaborative systems
  • live telemetry/control
  • voice or streaming AI workloads

Trade-offs

  • connection lifecycle is more complex
  • reconnect semantics matter
  • flow control/backpressure matter
  • horizontally scaled servers need connection-aware routing/state

Do not use bidirectional streams for ordinary CRUD simply because they are powerful.


Pattern 5: Work Queue

A queue distributes units of work to consumers.

Producer → Queue → Worker A
                 → Worker B
                 → Worker C

Typically, one logical message is processed by one consumer from a competing-consumer group.

Good fit

  • background jobs
  • email sending
  • image processing
  • invoice generation
  • import pipelines
  • workloads with traffic spikes

AWS guidance notes that asynchronous communication lets services process work according to their capacity and improves peak-load handling.

The queue becomes a buffer between arrival rate and processing rate.

Production concerns

  • duplicate delivery
  • visibility/acknowledgement semantics
  • dead-letter queues
  • retry policy
  • message age
  • idempotent consumers

A queue does not remove failures. It gives you a controlled place to absorb and retry them.


Pattern 6: Publish/Subscribe

Pub/sub is different from a work queue.

One event can be delivered to multiple independent subscribers.

OrderPlaced
    ↓
  Topic
  ├── Notifications
  ├── Analytics
  ├── Loyalty
  └── Warehouse

AWS defines pub/sub as a pattern that decouples publishers from interested subscribers through messaging infrastructure.

The publisher does not need to know which subscribers exist.

Good fit

  • domain events
  • fan-out
  • extensible event-driven systems
  • multiple reactions to the same fact

Main benefit

Adding a new subscriber does not require modifying the publishing service.

Main risk

Business behavior becomes distributed across subscribers and can be harder to understand end to end.


Pattern 7: Event Streaming

Event streaming keeps ordered event records available for consumers to read and often replay.

Apache Kafka describes event streaming as capturing, durably storing, processing, and routing continuous streams of events.

Conceptually:

Producer → Topic/Partition → durable event log
                              ↑      ↑
                          Consumer A Consumer B

Unlike a simple queue, consumers can often maintain their own positions and re-read history.

Good fit

  • event-driven data pipelines
  • CDC
  • analytics
  • state reconstruction
  • high-volume integration
  • multiple independent consumers

Trade-offs

  • partitioning/order design
  • consumer offsets
  • schema evolution
  • retention/storage
  • operational complexity

Use event streaming when durable history/replay is part of the requirement—not merely because you need asynchronous work.


Queue vs Pub/Sub vs Event Stream

They solve related but different problems.

NeedBetter starting pattern
One job handled by one workerWork queue
One event fan-outs to many consumersPub/sub
Durable ordered history + independent replayEvent stream
Immediate answer requiredREST/gRPC
Long multi-step business processWorkflow/orchestration

Real platforms may combine these patterns.


Pattern 8: Fire-and-Forget

The client submits work and only needs acknowledgement that the work was durably accepted.

POST request
   ↓
durable queue/job record
   ↓
202 Accepted + job_id

The important word is durably.

AWS guidance recommends not acknowledging fire-and-forget work until the request has been persisted or placed into a durable queue.

Otherwise:

return success
→ process crashes
→ work disappears

For user-visible operations, provide a status endpoint or event so the client can learn the final result.


Pattern 9: Async Request-Reply / Claim Check

Sometimes work is asynchronous but the caller eventually needs the result.

A common pattern:

POST /reports
→ 202 Accepted
→ job_id

GET /reports/{job_id}
→ queued / running / completed / failed

This is useful for:

  • long AI jobs
  • exports
  • large report generation
  • media processing

Polling should use sensible intervals/backoff rather than hammering the service continuously.


Pattern 10: Callback / Webhook

Instead of polling, the caller provides a callback target.

Client → start task + callback URL
Service processes task
Service → callback when complete

Good fit

  • cross-system integrations
  • long-running external jobs
  • payment/provider notifications

Security requirements

Treat callbacks as public APIs:

  • authenticate/sign payloads
  • validate destinations
  • retry safely
  • protect against SSRF when accepting URLs
  • use idempotent event IDs

Callbacks reduce polling but create delivery and security responsibilities.


Pattern 11: Choreography

In choreography, there is no central workflow controller.

Services react to events and emit more events.

OrderPlaced
    ↓
Inventory reserves stock
    ↓
InventoryReserved
    ↓
Payment captures funds
    ↓
PaymentCaptured

AWS notes that choreography supports loose coupling and independent service scaling.

Good fit

  • loosely coupled reactions
  • simple event chains
  • events with multiple independent consumers

Risks

  • workflow logic becomes scattered
  • debugging requires tracing across events
  • complex compensation becomes difficult

Choreography works best while the process remains understandable from its event contracts.


Pattern 12: Orchestration

Orchestration uses a central workflow controller.

Orchestrator
  ├── call inventory
  ├── call payment
  ├── wait for result
  ├── branch on failure
  └── compensate if required

AWS guidance recommends orchestration when workflows span multiple microservice boundaries and need centralized logic, branching, compensation, or visibility.

Good fit

  • multi-step business transactions
  • sagas
  • approval flows
  • long-running workflows
  • explicit compensation

Trade-off

The orchestrator becomes an important workflow dependency, so keep domain decisions in appropriate services rather than turning it into a new monolith.


Choreography vs Orchestration

A useful rule:

Prefer choreography when

  • the event is a fact many services may react to
  • reactions are mostly independent
  • coupling to a central workflow would be unnecessary

Prefer orchestration when

  • order of steps matters
  • compensation matters
  • a single workflow status is useful
  • complex branches must be explicit

Many good systems use both.


Data Consistency Changes With the Communication Pattern

Synchronous RPC often gives the caller immediate confirmation of one service's local state.

Async/event-driven systems commonly introduce eventual consistency.

Example:

Order Service: confirmed
Search/read model: still pending for 200 ms

The product must tolerate that delay.

Do not add async architecture to a workflow whose UX requires immediate global consistency without designing how that consistency is achieved.


Idempotency Is Mandatory Around Retries

A timeout does not prove that a remote operation failed.

Client sends CapturePayment
Server captures payment
Response lost
Client retries

Without idempotency, retries can duplicate side effects.

Use stable request/event IDs and enforce uniqueness around business actions.

This matters across REST, gRPC, queues, callbacks, and workflows—not only messaging.


Ordering Is a Business Requirement, Not a Broker Checkbox

Ask which events actually require order.

Example:

OrderCreated
OrderCancelled

may require ordering per order, but not globally across every order in the company.

Global ordering reduces parallelism.

Prefer the smallest ordering scope that preserves correctness, often via a partition key such as:

order_id
customer_id
tenant_id

Backpressure Must Exist Somewhere

When producers create work faster than consumers can process it:

incoming rate > processing rate

something has to happen.

Possible mechanisms:

  • queue buildup with bounded retention
  • producer throttling
  • rate limits
  • concurrency limits
  • load shedding
  • autoscaling consumers

An unbounded queue is only delayed failure.

Monitor both queue depth and oldest message age so you can distinguish harmless bursts from permanently falling behind.


Trace Across Async Boundaries

Distributed tracing is straightforward for synchronous HTTP and harder for asynchronous systems.

Carry trace/correlation metadata in events:

trace_id
correlation_id
causation_id
event_id

Then you can reconstruct:

API request
→ event publish
→ consumer
→ downstream API
→ database

Without this, an event-driven system becomes much harder to debug than a synchronous one.


A Decision Framework

Before choosing a communication pattern, ask:

  1. Does the caller require an immediate response?
  2. Can temporary inconsistency be tolerated?
  3. Is the operation high-volume or bursty?
  4. Does one message need one consumer or many?
  5. Must events be replayable?
  6. Is ordering required, and at what scope?
  7. Is the workflow multi-step with compensation?
  8. What happens when the receiver is unavailable?
  9. Can the operation be retried safely?
  10. How will operators trace the workflow?

The answers usually make the pattern much clearer.


Common Mistakes

Making everything synchronous

Creates long availability and latency chains.

Making everything event-driven

Creates unnecessary debugging and consistency complexity.

Using Kafka as a job queue by default

A durable event log and a simple work queue have different operational models.

Ignoring idempotency

Retries then become dangerous.

Requiring global ordering

Often destroys useful parallelism for no business reason.

Acknowledging async work before it is durable

Creates silent data loss during crashes.

Hiding workflow state in logs

Long workflows deserve explicit state.


Production Checklist

  • Communication style matches business latency requirements
  • Synchronous dependency chains are intentionally bounded
  • Async work is durably accepted before acknowledgement
  • Queue consumers are idempotent
  • Event contracts are versioned
  • Ordering scope is explicitly defined
  • Retries have deadlines/backoff/jitter
  • Pub/sub subscribers can fail independently
  • Long workflows have explicit status
  • Callbacks/webhooks are authenticated and retry-safe
  • Backpressure is measurable
  • Trace/correlation IDs cross async boundaries
  • Choreography/orchestration choice matches workflow complexity

Final Takeaway

Microservice communication should be chosen from the business interaction backward.

Use REST or gRPC when an immediate result is genuinely required. Use queues when work can be processed later by one consumer. Use pub/sub when many independent services need the same event. Use event streams when durable history and replay matter. Use orchestration when a multi-step workflow needs explicit sequencing and compensation.

The best architecture rarely uses one communication style everywhere. It uses several patterns deliberately, with clear failure, retry, ordering, and observability semantics around each boundary.

References

Tags:MicroservicesAPI DesigngRPCMessagingEvent-Driven ArchitecturePub/SubDistributed Systems
ENTERPRISE SAAS ENGINEERING

BUILDING A SAAS PRODUCT?

Explore Lofingo’s end-to-end SaaS platforms, multi-tenant cloud architecture, and high-concurrency backend engineering.

EXPLORE SAAS SERVICES →