10 Essential Microservice Design Patterns Every Backend Engineer Should Know
This comprehensive guide explains microservice architecture, its key characteristics, advantages, disadvantages, and when to adopt it, then details ten crucial design patterns—including database per service, event sourcing, CQRS, Saga, BFF, API gateway, Strangler, circuit breaker, externalized configuration, and consumer‑driven contract testing—complete with pros, cons, use‑cases, and technology examples.
Since the early days of software development (1960s), handling complexity in large software systems has been a daunting task. Over the years, software engineers and architects have tried many approaches: David Parnas' modularization and encapsulation (1972), Edsger W. Dijkstra's separation of concerns (1974), and Service‑Oriented Architecture (SOA, 1988). All of these relied on the mature divide‑and‑conquer technique.
From 2010 onward, these techniques proved insufficient for web‑scale or modern enterprise applications, prompting architects to develop a new modern approach: microservice architecture. While it continues the divide‑and‑conquer philosophy, it implements it in a brand‑new way.
Software design patterns provide reusable, proven solutions to common design problems, allowing teams to share a common vocabulary and avoid reinventing the wheel. Below is a brief introduction to microservice architecture.
By reading this article you will learn:
Microservice architecture
Advantages of microservice architecture
Disadvantages of microservice architecture
When to use microservice architecture
The most important microservice‑architecture design patterns, including their pros and cons, use cases, context, technology‑stack examples, and resources, are presented. Most of these patterns also appear in other contexts and can be used outside microservices, but they are described here specifically for microservices.
1 Microservice Architecture
I have previously written detailed overviews in the blogs “Microservice Architecture Overview and Why You Should Use It in Your Next Project” and “Is the Monolithic Software Architecture Really Over?”. If you are interested, you can read those posts for deeper insight.
What is microservice architecture? My definition is as follows:
Microservice architecture means splitting a large, complex system into smaller subsystems based on functionality or business needs. Each subsystem runs as an independently deployed process and communicates with others via lightweight, language‑agnostic synchronous (e.g., REST, gRPC) or asynchronous (messaging) network calls.
Below is a component view of a commercial web application built on microservice architecture:
Key characteristics of microservice architecture
The entire application is split into independent subprocesses, each containing multiple internal modules.
Unlike modular monoliths or SOA, microservices are vertically split by business domain.
Microservice boundaries are external; services communicate over the network (RPC or messaging).
Each microservice runs as an independent process and can be deployed independently.
Communication is lightweight and does not require a smart communication channel.
Advantages of microservice architecture
Better development scalability.
Faster development speed.
Supports iterative or modern incremental development.
Leverages modern software ecosystem advantages (cloud, containers, DevOps, serverless).
Supports horizontal and fine‑grained scaling.
Smaller codebases reduce cognitive complexity for developers.
Disadvantages of microservice architecture
Higher number of active components (services, databases, processes, containers, frameworks).
Complexity shifts from code to infrastructure.
Significant increase in RPC calls and network communication.
System‑wide security management becomes more challenging.
Overall system design becomes more difficult.
Introduces distributed‑system complexity.
When to use microservice architecture
Large‑scale web application development.
Cross‑team enterprise‑level application collaboration.
Long‑term benefits outweigh short‑term gains.
Team has senior architects or engineers capable of designing microservices.
2 Microservice Architecture Design Patterns
Database per Microservice
When a company replaces a monolith with a set of microservices, the most important decision is about the database. Monoliths typically use a single large central database. Even after moving to microservices, many architects keep the same database, which is a short‑term gain but a long‑term anti‑pattern, especially at scale, because services become tightly coupled through the database.
The better approach is to give each microservice its own data store, eliminating strong coupling at the database layer. Logical data isolation can be achieved even if services share a physical database by using separate schemas, collections, or tables, which also aligns with domain‑driven design.
Advantages
Data is fully owned by the service.
Coupling between development teams is reduced.
Disadvantages
Data sharing between services becomes more challenging.
Ensuring ACID transactions across the application becomes difficult.
Designing how to split the monolithic database is a highly challenging task.
When to use
In large enterprise applications.
When teams need full control over services to achieve scale and speed.
When not to use
In small‑scale applications.
When a single team develops all microservices.
Technology examples
All SQL and NoSQL databases provide logical data separation (e.g., separate tables, collections, schemas).
Event Sourcing
In microservice architectures that use dedicated databases, services need to exchange data. For highly scalable, fault‑tolerant systems, asynchronous event exchange is preferred. Traditional relational databases cannot support two‑phase locking at scale, while many NoSQL databases do not support distributed transactions.
In such scenarios, the Event Sourcing pattern stores a series of immutable events that represent state changes instead of storing the current state directly. The entire history of an entity can be reconstructed by replaying events, allowing services to compute required data states from the event store.
Advantages
Provides atomic operations for highly scalable systems.
Automatically records entity change history with temporal replay.
Enables loosely coupled, event‑driven microservices.
Disadvantages
Reading entities from the event store becomes a new challenge, often requiring a separate read model (CQRS).
Overall system complexity increases, usually requiring domain‑driven design.
System must handle event duplication (idempotency) or loss.
Changing event structures is an additional challenge.
When to use
High‑scalable transactional systems using relational databases.
Transactional systems using NoSQL databases.
Elastic, highly scalable microservice architectures.
Message‑driven or event‑driven systems (e‑commerce, booking, reservation).
When not to use
Low‑scalable transactional systems using SQL databases.
Simple microservice architectures where services can synchronously exchange data via APIs.
Technology examples
Event stores: EventStoreDB, Apache Kafka, Confluent Cloud, AWS Kinesis, Azure Event Hub, GCP Pub/Sub, Azure Cosmos DB, MongoDB, Cassandra, Amazon DynamoDB. Frameworks: Lagom, Akka, Spring, akkatecture, Axon, Eventuate.
Command and Query Responsibility Segregation (CQRS)
If we use Event Sourcing, reading entities from the event store becomes difficult. CQRS separates the write side (commands) from the read side (queries). There are simple and advanced forms.
In the simple form, different models are used for reads and writes:
It reinforces the Single Responsibility Principle and separation of concerns, leading to cleaner design.
In the advanced form, separate data stores are used for reads and writes, often combined with Event Sourcing. Write stores are the system of record; read stores are optimized for queries.
Advantages
Faster data reads in event‑driven microservices.
High data availability.
Read and write systems can scale independently.
Disadvantages
Read stores are eventually consistent.
Overall system complexity increases; a poorly designed CQRS can jeopardize the project.
When to use
In highly scalable microservice architectures using Event Sourcing.
When complex domain models require multiple data stores for reads.
When read/write load differs significantly.
When not to use
When a simple microservice can synchronously exchange data via APIs.
Technology examples
Write stores: EventStoreDB, Apache Kafka, Confluent Cloud, AWS Kinesis, Azure Event Hub, GCP Pub/Sub, Azure Cosmos DB, MongoDB, Cassandra, Amazon DynamoDB. Read stores: Elasticsearch, Solr, Cloud Spanner, Amazon Aurora, Azure Cosmos DB, Neo4j.
Saga
If microservices use dedicated databases, managing consistency with distributed transactions is a major challenge because traditional two‑phase commit does not scale for relational databases and is unsupported by most NoSQL databases.
The Saga pattern provides a way to achieve distributed transactions. A Saga is a sequence of local transactions, each performed in a separate microservice, that publish an event or message upon completion. The first transaction is triggered by an external request; subsequent transactions are triggered by the messages emitted by previous ones.
If a local transaction fails, the Saga executes compensating transactions to roll back earlier changes.
Saga coordination can be choreographed (each service listens to events) or orchestrated (a central coordinator tells services what to do).
Advantages
Provides consistent transactions for highly scalable, loosely coupled, event‑driven microservices.
Works with non‑relational databases that do not support 2PC.
Disadvantages
Requires handling of transient failures and idempotency.
Debugging is difficult and complexity grows with the number of services.
When to use
In high‑scalable, loosely coupled microservices that use Event Sourcing.
In systems that use distributed non‑relational databases.
When not to use
In low‑scalable transactional systems using relational databases.
When services have circular dependencies.
Technology examples
Axon, Eventuate, Narayana.
Backend‑for‑Frontend (BFF)
In modern commercial applications, especially those built with microservices, the front‑end and back‑end are separate services communicating via APIs or GraphQL. When a mobile client also exists, the Web UI and mobile UI have different screen sizes, performance, power, and bandwidth requirements, leading to different API needs.
The BFF pattern is suitable when a dedicated back‑end is needed for a specific UI. It also provides benefits such as encapsulating downstream microservices, reducing frequent UI‑to‑service communication, and adding an extra security layer for DMZ‑deployed services.
Advantages
Separates concerns, allowing UI‑specific optimizations.
Provides higher security.
Reduces frequent communication between UI and downstream services.
Disadvantages
Potential code duplication between BFFs.
Many BFFs may be needed for different UIs (smart TV, web, mobile, desktop).
Requires careful design; BFF should contain only UI‑specific logic, not business logic.
When to use
When an application has multiple UIs with different API requirements.
When additional security layers between UI and downstream services are needed.
When using micro‑frontends.
When not to use
If multiple UIs share the same API.
If core microservices are not deployed in a DMZ.
Technology examples
Any back‑end framework (Node.js, Spring, Django, Laravel, Flask, Play, …) can support BFF.
API Gateway
In microservice architectures, the UI often needs to call many fine‑grained services, which can become complex and challenging. Additional cross‑cutting concerns such as SSL termination, authentication, authorization, throttling, and logging are also required.
An API gateway acts as a façade between client apps and backend microservices. It can route requests, fan‑out to multiple services, aggregate responses, and handle cross‑cutting concerns.
Advantages
Provides loose coupling between front‑end and back‑end services.
Reduces the number of client‑to‑service calls.
Enables high security via SSL termination, authentication, and authorization.
Centralizes cross‑cutting concerns such as logging, monitoring, throttling, and load balancing.
Disadvantages
Can become a single point of failure.
Additional network hop adds latency.
If not scaled, it can become a bottleneck.
Increases maintenance and development cost.
When to use
In complex microservice architectures where it is almost mandatory.
In large enterprises where centralized security and cross‑cutting concerns are essential.
When not to use
In private projects or small companies where security and central management are not top priorities.
When the number of microservices is very small.
Technology examples
Amazon API Gateway, Azure API Management, Apigee, Kong, WSO2 API Manager.
Strangler
When migrating an existing monolith to microservices, the challenge is to replace functionality incrementally without breaking availability. The Strangler pattern solves this by gradually replacing parts of the monolith with new microservices while routing traffic through a façade (often an API gateway). Once all functionality has been migrated, the monolith is retired.
Advantages
Safe migration of monolith to microservices.
Allows parallel development of new features and migration of existing ones.
Provides better control over migration pace.
Disadvantages
Sharing data storage between legacy monolith and new microservices is challenging.
Adding a façade introduces latency.
End‑to‑end testing becomes more difficult.
When to use
When incrementally migrating a large backend monolith to microservices.
When not to use
If the monolith is small, a full replacement may be simpler.
If client requests cannot be intercepted to route to the façade.
Technology examples
API gateway backend frameworks.
Circuit Breaker
In microservice architectures, services often call each other synchronously. Transient failures (slow network, timeouts) can cause repeated retries, wasting resources. Severe failures can cause long‑lasting outages and cascade failures. A circuit breaker prevents this by quickly failing calls after a threshold of recent failures.
The circuit breaker has three states:
Closed – requests are routed normally; failures are counted. If failures exceed a threshold, the breaker opens.
Open – requests fail fast; after a timeout the breaker moves to half‑open.
Half‑open – a limited number of requests are allowed; if they succeed the breaker closes, otherwise it reopens.
Advantages
Improves fault tolerance and resilience of microservice architectures.
Prevents cascading failures across services.
Disadvantages
Requires complex exception handling.
Needs logging and monitoring.
Should support manual reset.
When to use
In tightly coupled microservice architectures that use synchronous communication.
When a service depends on multiple other services.
When not to use
In loosely coupled, event‑driven microservice architectures.
When a service does not depend on other services.
Technology examples
API gateways, service meshes, and circuit‑breaker libraries (Hystrix, Resilience4j, Polly).
Externalized Configuration
Every business application has many configuration parameters for infrastructure (databases, network, service addresses, credentials, certificates). Storing these inside the application (hard‑coding) is a bad practice that creates security risks and forces rebuilds for any change, which is especially problematic with hundreds of microservices.
A better approach is to externalize all configuration, keeping build processes separate from runtime environments and loading production configuration at runtime via environment variables or external files, minimizing security exposure.
Advantages
Production configuration is not part of the codebase, reducing security vulnerabilities.
Changing configuration does not require rebuilding the application.
Disadvantages
Requires choosing a framework that supports externalized configuration.
When to use
Any important production application should use externalized configuration.
When not to use
During proof‑of‑concept development.
Technology examples
Almost all modern enterprise frameworks support externalized configuration.
Consumer‑Driven Contract Testing
In microservice architectures, many services built by different teams need to collaborate. Integration testing of consumer services is challenging; test doubles are fast but may not represent real providers, and they cannot detect provider API changes. End‑to‑end tests are fragile, slow, and expensive.
Consumer‑driven contract testing addresses this by having consumer teams write test suites that capture their requests and expected responses or messages (the contract). Provider services then run these contract suites as part of their automated tests, automatically verifying compatibility.
Advantages
Detects unexpected provider API or message changes quickly.
Reduces surprises and increases robustness, especially in large enterprise applications.
Improves team autonomy.
Disadvantages
Requires extra effort to develop and integrate contract tests on the provider side, possibly using different tooling.
If contracts diverge from real behavior, production failures may occur.
When to use
In large enterprise business applications where different teams develop different services.
When not to use
For small, simple applications where all microservices are owned by the same team.
When provider services are relatively stable and not under active development.
Technology examples
Pact, Postman, Spring Cloud Contract.
Summary
In modern large‑scale enterprise software development, microservice architecture helps achieve scalability and long‑term benefits, but it is not a universal silver bullet; applying it to the wrong type of application can cause more harm than good. Development teams should follow best practices and reuse a set of proven design patterns.
The most critical design pattern in microservice architecture is the database‑per‑service pattern. Implementing it is challenging and often requires related patterns such as event‑driven communication, CQRS, and Saga. In typical business applications with multiple clients (Web, Mobile, Desktop, Smart Devices), the communication volume between clients and microservices can be high, and a unified security control is needed; therefore Backend‑for‑Frontend and API gateway designs are valuable. The circuit‑breaker pattern greatly aids error handling. Migrating legacy monoliths to microservices is challenging, and the Strangler pattern helps accomplish this. Consumer‑driven contract testing is a foundational pattern for microservice integration testing, and externalized configuration is a must‑have pattern for any modern application.
This series is not exhaustive; real‑world scenarios may require additional patterns, but it provides an excellent introduction to microservice architecture design patterns.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITFLY8 Architecture Home
ITFLY8 Architecture Home - focused on architecture knowledge sharing and exchange, covering project management and product design. Includes large-scale distributed website architecture (high performance, high availability, caching, message queues...), design patterns, architecture patterns, big data, project management (SCRUM, PMP, Prince2), product design, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
