Skip to main content

Microservices Patterns

Microservices architecture introduces a set of recurring challenges that are fundamentally different from those in monolithic systems. Network boundaries, independent data stores, and asynchronous communication create problems around distributed transactions, partial failures, and complex service coordination. Microservices patterns are battle-tested solutions to these problems. They provide architects and engineers with a shared vocabulary and a set of design heuristics to navigate the trade-offs inherent in distributed systems.

However, patterns are not universal recipes. Every pattern introduces its own complexity and operational overhead. The goal is not to apply every pattern you learn, but to understand the problem a pattern solves well enough that you can decide whether the cost of implementing it is justified in your context. This section of the handbook catalogues the most important patterns, grouped by the problems they address, so you can find the right tool for the right job.

Why Microservices Patterns Matter​

When you move from a single process to a network of cooperating services, you encounter challenges that were previously invisible:

  • Communication complexity: Services must discover each other and exchange data over unreliable networks.
  • Distributed transactions: A business operation that spans multiple services cannot rely on a single ACID database transaction.
  • Data consistency: Maintaining a consistent view of data across services requires careful orchestration or event-driven eventual consistency.
  • Network failures: Calls between services can fail, time out, or return stale data. The system must degrade gracefully.
  • Scalability and deployment: Different parts of the system may need to scale independently and be deployed without coordinating with the rest.
  • Legacy system migration: Moving from a monolith without a big-bang rewrite demands incremental, low-risk strategies.

Patterns give you a language to reason about these challenges. Saying “let’s use a Saga for the checkout flow” immediately communicates the consistency model, the compensation logic, and the expected failure modes to the team. Good architecture is not about collecting patterns; it is about choosing the right pattern for the right problem, and knowing when no pattern is the best pattern.

Pattern Categories​

The patterns in this section are organised into categories that reflect the main architectural concerns in a microservices system.

CategoryProblems SolvedExamples
Decomposition PatternsBreaking systems into manageable services and migrating legacy systemsAPI Gateway, Backend for Frontend, Strangler Fig
Data Consistency PatternsManaging transactions and state across distributed data storesSaga, CQRS, Event Sourcing, Transactional Outbox
Communication PatternsConnecting services reliably and safelyMessaging, Events, Service Discovery
Resilience PatternsHandling and isolating failuresCircuit Breaker, Retry, Timeout, Bulkhead
Integration PatternsIntegrating with external or legacy systems while preserving your modelAnti-Corruption Layer, Adapter

Decomposition Patterns​

Decomposition patterns define how a system is structured into services and how clients interact with them. They also provide strategies for evolving an existing system toward a new architecture without a complete rewrite.

API Gateway Pattern​

An API Gateway acts as a single entry point for all clients. It routes requests to the appropriate backend service, handles cross-cutting concerns such as authentication, rate limiting, and request/response transformation, and can aggregate data from multiple services. It simplifies client code and centralises governance.

Use cases: Almost every microservices system uses some form of API Gateway, whether implemented as a dedicated service or provided by a cloud-managed solution.

Backend for Frontend (BFF)​

When a system has multiple client types—such as a web application, a mobile app, and an IoT device—each client often requires different data shapes and interaction patterns. The BFF pattern creates a dedicated backend service for each client type, optimising the API surface and reducing frontend complexity. The BFF owns its own logic and can aggregate, filter, or enrich data specifically for its client.

Strangler Fig Pattern​

Inspired by the vine that gradually replaces its host tree, the Strangler Fig pattern allows you to incrementally migrate a monolith to microservices. You intercept requests to the monolith and route them to new service implementations as they become available. Over time, the monolith is “strangled” until it can be retired. This approach reduces migration risk by delivering value incrementally.

Data Consistency Patterns​

Distributed data management is widely recognised as the hardest problem in microservices. These patterns provide mechanisms to maintain consistency without relying on distributed transactions.

Saga Pattern​

A saga manages a long-lived business transaction that spans multiple services. Each step in the saga performs a local transaction and publishes an event (choreography) or receives a command (orchestration). If a step fails, the saga executes compensating transactions to undo the preceding steps, achieving eventual consistency.

CQRS Pattern​

Command Query Responsibility Segregation separates the model used to update information (commands) from the model used to read information (queries). This allows you to optimise each side independently: the write side can enforce business invariants, while the read side can be denormalised and tuned for specific query patterns. CQRS is often paired with event sourcing but can be applied on its own.

Transactional Outbox Pattern​

A service that writes to its database and then publishes a message to a broker faces a dual-write problem: one may succeed while the other fails. The transactional outbox pattern writes the outgoing message to a table within the same database transaction as the business data. A separate process reads the outbox table and reliably publishes the messages to the broker.

Event Sourcing Pattern​

Instead of storing the current state of an entity, event sourcing persists every state-changing event as an append-only log. The current state is reconstructed by replaying events. This pattern provides a complete audit trail, enables temporal queries, and integrates naturally with event-driven architectures, but it adds significant complexity.

Resilience Patterns​

Distributed systems must assume that failures will happen. Resilience patterns prevent local failures from cascading into system-wide outages.

Circuit Breaker Pattern​

A circuit breaker monitors a failing external service. When the failure rate exceeds a threshold, the breaker trips and immediately returns an error without further attempts. After a cooldown period, the breaker moves to a half-open state and allows a limited number of test requests. If they succeed, the breaker closes; if they fail, it opens again.

Retry, Timeout, and Fallback​

These are foundational resilience tactics. Retries handle transient failures by re-attempting an operation with backoff. Timeouts prevent a caller from waiting indefinitely for a response that will never come. Fallbacks provide a degraded response when a dependency is unavailable, such as serving a cached value or returning a default.

Bulkhead Pattern​

Inspired by the compartments of a ship, bulkheads partition resources—such as thread pools or connection pools—so that a saturated service does not consume all available resources and starve other services. This isolates the blast radius of a failure.

Communication and Integration Patterns​

How services talk to each other shapes their coupling, reliability, and scalability. The choice between synchronous request-response (REST, gRPC) and asynchronous messaging (events, commands) is one of the earliest and most consequential architectural decisions. Beyond the basic style, patterns like Service Discovery let services locate each other without hard-coded addresses. Anti-Corruption Layers protect your bounded context from the vocabulary and models of external or legacy systems by translating between them at the boundary. The Sidecar Pattern co-locates infrastructure concerns (such as proxies, logging, or configuration agents) with a service, often in a container orchestrator like Kubernetes.

How to Learn Microservices Patterns​

Patterns build on fundamentals. We recommend the following learning sequence:

  1. Understand service boundaries. You cannot choose decomposition patterns wisely if you do not understand what a good service boundary looks like. Start with the Foundations section.
  2. Learn API Gateway and communication patterns. These are the first patterns you will encounter when designing the system's surface area and inter-service interaction.
  3. Master distributed data patterns. Data is the hardest part. Once you understand how services communicate, you must decide how they keep data consistent.
  4. Learn resilience patterns. After communication and data are in place, you need to handle failures in those interactions.
  5. Study integration and migration patterns. Learn how to connect your system to the outside world and how to evolve legacy systems.
  6. Apply patterns through architecture scenarios. Consolidate your knowledge by solving realistic system design problems where multiple patterns must be combined.

Common Pattern Mistakes​

Patterns are powerful, but misapplied they can cause more harm than the problems they solve.

  • Applying patterns without understanding the problem. Using a Saga for a simple two-step flow that could be handled by a compensating REST call adds unnecessary complexity. Always start with the simplest solution.
  • Overusing Event Sourcing. Event sourcing is a niche pattern. Most services can track state with a simple CRUD model and use the transactional outbox for reliable messaging.
  • Creating too many services. Every pattern that suggests decomposition can be taken too far. A system of nano-services that must all be deployed together is worse than a modular monolith.
  • Ignoring operational complexity. Resilience patterns like retries and circuit breakers require tuning in production. Without observability, you are flying blind.
  • Treating patterns as frameworks. Patterns are design guidance. The concrete implementation in your language and stack will differ. Understand the principles so you can adapt the pattern to your context.

Pattern Decision Guide​

When faced with a specific problem, use this table as a starting point for your decision:

ProblemRecommended Pattern
Multiple clients need different APIsBackend for Frontend
Need a unified API entry point with cross-cutting concernsAPI Gateway
A business transaction spans multiple servicesSaga
Must guarantee an event is published after a DB writeTransactional Outbox
Read and write workloads have vastly different shapesCQRS
Integrating with a legacy or third-party systemAnti-Corruption Layer
Downstream service is failing and causing cascading timeoutsCircuit Breaker
Incrementally replacing a monolith with servicesStrangler Fig

Where to Go Next​

Patterns are one piece of the microservices puzzle. To see how they work in production and at scale, continue your learning with:

  • Engineering: Learn how to deploy, observe, test, and secure the services you have designed.
  • Architecture Scenarios: Work through full system design exercises that require combining multiple patterns to meet business requirements.
  • Interview: Prepare for senior engineering and architecture interviews by practicing how to discuss trade-offs and articulate your pattern choices.

Remember that patterns are not rules but tools for making better architectural decisions. The measure of a skilled architect is not how many patterns they have memorised, but how well they can reason about a system's needs and select the appropriate set of patterns—and how confidently they can leave the rest behind. Build your understanding, apply it deliberately, and always stay curious.