Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 54

Choosing Communication Patterns in Microservices

Recommended resources

Introduction

In our recent lessons, we've focused on the mechanics of high-performance synchronous communication with gRPC and the critical strategies for load balancing it. This naturally leads to a higher-level architectural question: is a synchronous, request-response pattern always the right choice for every interaction in a complex distributed system? As you move toward a Principal Engineer role, the ability to select and justify the appropriate communication pattern for a given scenario becomes paramount.

This lesson addresses that exact challenge. We will develop a comprehensive decision framework for choosing among different communication patterns in a microservices ecosystem. We'll move beyond a simple "sync vs. async" debate and build a multi-dimensional model based on three core forces: Communication Style (synchronous vs. asynchronous), Coordination Strategy (orchestration vs. choreography), and the resulting Consistency Model (atomic vs. eventual). By analyzing the interplay of these forces, you will gain a structured methodology for evaluating trade-offs and selecting the optimal pattern for any given business workflow.


The Core Tension: Synchronous vs. Asynchronous

At the heart of inter-service communication lies a fundamental choice: do services wait for each other, or do they work independently? The diagrams below illustrate the essential difference. Synchronous communication involves a blocking call, where Service A waits for a response from Service B. Asynchronous communication involves Service A sending a message and moving on, decoupling it from Service B.

This diagram contrasts the blocking nature of synchronous calls with the non-blocking, queue-based approach of asynchronous communication. It also previews some of the protocols associated with each style, like REST/gRPC for synchronous and AMQP/MQTT for asynchronous.

This choice between a direct call (like RPC) and indirect messaging is one of the most consequential you'll make in system design. It impacts availability, scalability, and complexity.

RPC vs Messaging: When to use which?

To start, let's get a practical, opinionated perspective on when to use each style. The video "RPC vs Messaging: When to use which?" from CodeOpinion offers a clear heuristic.

Please watch the following segments: The initial distinction between synchronous (HTTP/RPC) and asynchronous (messaging). The role of commands vs. queries, especially for client interactions. The resilience argument for using asynchronous messaging between internal services, highlighting how it handles transient failures more gracefully.

The video advocates a powerful starting point for our framework:

  • Queries from a client to your backend are often necessarily synchronous. The user is waiting for data to render a UI.
  • Communication between your internal services should default to asynchronous messaging to improve resilience and decoupling. If Service B is down, Service A can still successfully place a message in a queue and move on. The system as a whole remains more available.

This separation of concerns—how clients talk to the system versus how services talk to each other—is a key insight.


The Three Forces of Service Interaction

While sync versus async is the initial fork in the road, a robust decision framework requires more nuance. Any business process that spans multiple services is governed by three interacting forces, which create a spectrum of architectural patterns.

Designing Distributed Systems: Sagas and Trade-Offs

The article "Designing Distributed Systems: Sagas and Trade-Offs" provides an excellent conceptual model for this.

Please read the introductory section, The Three Forces. This will introduce the core concepts of Communication, Coordination, and Consistency that we will build upon.

As the article lays out, our decision framework must balance:

  1. Communication: The protocol and blocking semantics (synchronous RPC vs. asynchronous messaging).
  2. Coordination: The workflow logic (centralized orchestration vs. decentralized choreography).
  3. Consistency: The data guarantees across services (atomic vs. eventual).

Let's dissect each of these forces.

Force 1: Communication (Sync vs. Async Revisited)

We've established the basic difference. Now, let's explore the trade-offs in more detail, as they apply to a real business scenario.

Event-Driven Architecture (EDA) vs Request/Response (RR)

The video "Event-Driven Architecture (EDA) vs Request/Response (RR)" from Confluent provides a great walkthrough using a common e-commerce example.

Watch the comparison from the main example. Focus on the six factors the presenter uses to compare the two architectures: Reactivity Coupling (and the implication for eventual consistency) Historical State Architectural Flexibility Data Access and Reuse

The video highlights a critical benefit of asynchronous, event-driven patterns: data reuse. An event stream like "Orders" can be consumed by the fulfillment service, a fraud detection service, a business analytics pipeline, and an ML model, all without the producing service ever knowing about them. In a synchronous RPC world, you would have to either modify the Order service to call all these downstream systems (creating tight coupling) or have each system poll the Order service's database (creating a huge burden).

Force 2: Coordination (Orchestration vs. Choreography)

When a business process involves a sequence of steps across multiple services (e.g., Create Order -> Process Payment -> Update Inventory -> Ship Order), how is that flow managed?

  • Orchestration: A central service, the orchestrator, acts like a conductor. It explicitly calls each service in the required order, handles logic, and manages the overall state of the workflow.
  • Choreography: There is no central conductor. Each service performs its task and publishes an event (e.g., OrderPlaced). Other services subscribe to these events and react accordingly, triggering the next step in the process.

Designing Distributed Systems: Sagas and Trade-Offs

Let's return to the "Sagas and Trade-Offs" article for a detailed look at these two coordination patterns.

Please read the section on Coordination. Pay close attention to the diagrams and the trade-off tables for both Orchestration and Choreography. Note how orchestration provides a single source of truth but can be a bottleneck, while choreography is more decoupled but harder to observe and debug.

The choice here is a classic trade-off between control and decoupling. Orchestration gives you explicit control and visibility, making it easier to reason about complex workflows. Choreography gives you maximum flexibility and scalability, as services are completely independent, but understanding the end-to-end flow requires piecing together interactions across multiple event streams.

Force 3: Consistency (Atomic vs. Eventual)

This brings us to the data guarantees. In a monolith with an ACID database, a multi-step process can be wrapped in a single transaction; it either all succeeds or all fails. In a distributed system, this is much harder.

  • Atomic Consistency: The goal is to simulate an ACID transaction across services. If any step fails, compensating actions are triggered to "undo" the previous steps, leaving the system in a consistent state. This provides an all-or-nothing guarantee to the user but is complex and can reduce availability.
  • Eventual Consistency: The system accepts that there will be brief periods of inconsistency. Each service commits its state locally and the overall system state converges over time as events are processed. This maximizes availability and scalability but requires the business and UI to handle transient states.

Your experience with distributed systems has already exposed you to these concepts, which you'll recognize as the shift from ACID to BASE properties (Basically Available, Soft state, Eventual consistency).


The Decision Framework: 8 Saga Patterns

Now, we can synthesize these three forces into a concrete decision-making tool. A Saga is a design pattern for managing a sequence of local transactions across microservices. The different ways of combining Communication, Coordination, and Consistency give rise to a spectrum of saga patterns, each with distinct characteristics.

Designing Distributed Systems: Sagas and Trade-Offs

The "Sagas and Trade-Offs" article categorizes these combinations into eight named patterns. This taxonomy provides the vocabulary and framework for making and defending your architectural choices.

Read the final major section of the article, Saga Patterns. You don't need to memorize every pattern, but focus on understanding how the trade-offs (Coupling, Complexity, Availability, Scale) change for these four key patterns: Epic Saga (Sync, Atomic, Orchestrated): The most monolith-like. Fairy-Tale Saga (Sync, Eventual, Orchestrated): A very common and pragmatic choice. Parallel Saga (Async, Eventual, Orchestrated): Excellent for scalable, controlled backend workflows. Anthology Saga (Async, Eventual, Choreographed): The most decoupled, but most complex to observe. Also, take note of the Horror Story Saga and why it's considered an anti-pattern.

This framework of saga patterns is incredibly powerful. Instead of a vague discussion, you can now make precise statements like, "For the user checkout flow, I propose a Fairy-Tale Saga. We need an orchestrator for visibility and synchronous calls to provide immediate feedback to the user, but we can accept eventual consistency and avoid a distributed transaction to maximize availability."


Summary and A Practical Heuristic

We have constructed a multi-dimensional framework for selecting communication patterns. The choice isn't just sync vs. async; it's a trade-off across Communication, Coordination, and Consistency.

This chart summarizes the high-level characteristics of three primary communication technologies: REST (synchronous), Messaging (asynchronous), and gRPC (synchronous). It serves as a good reference for the technical underpinnings of our framework.

To make this actionable, here is a heuristic decision process a Principal Engineer might follow:

  1. Analyze the Business & User Experience:

    • Does the user require immediate, deterministic feedback (e.g., payment confirmation)? This pushes you towards synchronous communication.
    • Can the operation happen in the background (e.g., starting a VM, processing a video)? This strongly suggests an asynchronous pattern. The client makes one sync call to submit the job and is then notified of completion later.
  2. Analyze the Workflow & Control Requirements:

    • Is the business process complex, with many steps, conditional logic, or a need for central auditing? This points towards Orchestration (e.g., Parallel Saga, Fairy-Tale Saga).
    • Is the process simple, or is maximum service autonomy and scalability the top priority? This points towards Choreography (e.g., Anthology Saga).
  3. Analyze the Consistency & Fault Tolerance Needs:

    • Is atomic, all-or-nothing behavior an absolute business requirement? Be prepared for the costs of an Epic Saga—question this requirement vigorously. The complexity and availability hit are severe.
    • In almost all other cases, design for Eventual Consistency. This is the native state of scalable, distributed systems. Choose the saga pattern that provides the right level of control for your eventually-consistent workflow.

Conclusion

In this lesson, we built a robust decision framework for one of the most fundamental aspects of distributed system design: inter-service communication.

Key Takeaways:

  • The choice of communication pattern is a trade-off between three forces: Communication style (sync/async), Coordination strategy (orchestration/choreography), and Consistency model (atomic/eventual).
  • Synchronous RPC is best suited for client-facing queries or when immediate feedback is essential. However, it creates tight temporal coupling, which can reduce availability.
  • Asynchronous Messaging should be the default for internal service-to-service communication, as it maximizes decoupling, resilience, and scalability.
  • The Saga pattern provides a vocabulary for managing multi-step business processes. The eight named sagas represent different points on the trade-off spectrum, from the tightly-coupled Epic Saga to the highly-decoupled Anthology Saga.
  • As a Principal Engineer, your role is not to advocate for one "best" pattern, but to select and justify the right combination of trade-offs for each specific business workflow.

We've now explored what patterns to use and why. But as systems grow, managing the operational complexity of hundreds of services communicating—especially with synchronous RPC—becomes a major challenge. In our next lesson, we will examine service mesh architecture, a technology layer designed specifically to address this complexity by managing traffic, security, and observability for service-to-service communication.