Microservices Design Patterns
Introduction to Microservices
Microservices are self-contained and independent deployment modules. In Microservices,
an
the application is divided into independent modules based on business domains.
Microservices are designed based on Domain Driven Design (DDD), which says the
application should be modeled around independent modules with bounded context for the
m
specific business domain.
.a
As we now have independent modules, we have faster rollout and quicker release cycles. All
the modules can be worked on in parallel, and any changes will only affect that specific
ed
module. Therefore, Continuous Integration (CI) & Continuous Delivery (CD) are key
advantages of using Microservices architecture.
er
As monolithic applications pose challenges — why microservices (four
concepts in the source)
ilt
Some of the key points about Microservices (from your document):
nf
1. Loosely coupled components designed around business domains
.u
2. Application can be distributed across different clouds and data centers
3. Change management is easy in Microservices
its
In short, the Microservices architectural style is an approach to developing a single application
as a suite of small services, each running in its own process and communicating with
@
lightweight mechanisms, often an HTTP resource API. These services are built around
business capabilities and are independently deployable by fully automated deployment
machinery.
— Martin Fowler (as cited in the original whitepaper)
an
m
.a
ed
er
ilt
Microservices design patterns and principles
nf
As we now know, Microservices are independent components that encapsulate business
.u
domains. If applications are architected using Microservices design principles and patterns,
then the application is highly scalable.
its
Principles on which microservices architecture is built (complete list from
source)
@
1. Scalability
2. Flexibility
3. Independent and autonomous
4. Decentralized governance
5. Resiliency
6. Failure isolation
7. Continuous delivery through the DevOps
While adhering to the principles mentioned above, there are some standard sets of problems
& complex scenarios that architects and developers need to address. In addressing such
common problems, statements bring proven solutions to the table, which become patterns.
an
m
.a
ed
er
ilt
nf
.u
Here are a few high-level patterns that are used around Microservices (conceptually — the
canonical visual catalogue is linked below).
its
Picture reference (from source): [Link] — Microservice architecture patterns
@
We will cover the most used and discussed design patterns in detail, along with use cases.
Here is the list of design patterns that will be covered (exactly as in the attached document):
1. Database per Microservices
2. CQRS
3. Event Sourcing
4. Saga
5. BFF
6. API Gateway
7. Strangler
8. Circuit Breaker
9. Externalized Configuration
10.Consumer-Driven Contract Tracing (title as in source; content is Consumer-Driven
Contract Testing — see §10)
an
One-page pattern map (quick revision)
m
# Pattern Problem it solves Watch out for
.a
1 ed
Database per service Hidden coupling via
shared DB
Cross-service queries,
reporting, transactions
er
2 CQRS Read/write contention, Sync complexity, eventual
ilt
heavy joins consistency
nf
3 Event sourcing Audit, replay, lossless Schema evolution, storage,
.u
history complexity
its
4 Saga Distributed Compensation design,
@
“transactions” choreography vs orchestration
5 BFF Many backends, Duplication, “second monolith”
different UIs
6 API gateway Single entry, SPOF, fat gateway
cross-cutting concerns
7 Strangler Big-bang monolith Dual run, routing, data split
migration
an
8 Circuit breaker Cascading failures Blind retries, hiding errors
m
9 Externalized Many instances, env Secrets handling, refresh
.a
configuration drift semantics
10
ed
Consumer-driven Breaking API changes CI discipline, contract
er
contract testing ownership
ilt
nf
1. Database per Microservices
.u
Microservices are generally independent and loosely bound components. So, to achieve this
independent nature, every Microservice must have its own database so that it can be
its
developed and deployed independently.
Let’s take the example of an e-commerce application. We will have product, ordering, and
@
SKU Microservices — each service interacts (store and retrieve) with data from their own
databases. Any changes to one database don’t impact other Microservices.
Other Microservices can’t directly access each independent database. Each component’s
persistent data can only be accessed via APIs. So, Database per Microservices provides
many benefits, especially to evolve rapidly and support massive scaling as they make each
Microservice independent.
Database changes can be made independently without impacting other Microservices:
● Each database can scale independently
● Microservices domain data is encapsulated within the service
● If one of the database servers is down, this will not affect other services
an
m
.a
ed
er
ilt
nf
.u
its
Also polyglot data persistence gives the ability to select the best-optimized storage needs
per Microservices. Example from the source (e-commerce):
@
● The product Microservice uses a NoSQL document database for catalog-related data
(e.g. JSON objects) to accommodate high volumes of read operations
● The shopping cart Microservice uses a distributed cache that supports a simple
key-value datastore
● The ordering Microservice uses a relational database to accommodate the rich
relational structure of its underlying data
Because of the ability to scale and high availability, NoSQL databases are getting highly
popular and are widely used in enterprise applications nowadays. Also, their support for
unstructured data gives flexibility to developments on Microservices components.
an
m
.a
ed
Interview stripIn one line: Each service owns its data; others access only via API/events.
Say: “Shared DB across services is the distributed monolith smell.”
er
ilt
2. CQRS design pattern
nf
CQRS stands for Command Query Responsibility Segregation. It is one of the most widely
.u
used patterns for querying the database in Microservices architecture. CQRS is very handy
when we need to eliminate complex queries involving inefficient joins. This pattern promotes
its
the complete separation of read and write concerns in the database.
@
Traditionally, in Monolithic or SOA architecture, we have a single database for the entire
application, and these databases will respond to both read and write requests. As the
application becomes more complex over time, reading and writing to the database become
non-performant. Sometimes applications follow an active-passive database model; however,
even then, only one database copy is active at a time, so performance issues still persist.
Examples from the source:
● For reads: a query that needs to join more than ten tables can lead to locking the
database due to query latency
● For writes: complex validations and lengthy business logic for some CRUD operations
can also cause locking of database operations
So, reads and writes are different operations for which separate strategies can be defined.
CQRS applies separation of concerns and separates reads and writes into two databases.
You can use different database types for reading vs writing (example from source: NoSQL for
an
reading, relational for CRUD / writes).
Another factor is the nature of the application. If use cases mostly read compared to writing,
m
the application is read-intensive — you should focus and choose read/write databases
accordingly.
.a
CQRS separates reads and writes into different databases where:
ed
● Commands perform the creation or updating of data
● Queries perform read data
er
Commands are actions with defined operations like “add the item to bucket” or “check my
balance.” Commands can be published via message brokers, which help applications process
ilt
them in an async manner. Queries never modify the database. Queries return JSON data
nf
with DTO objects. In this way, we isolate Commands and Queries.
How to sync databases with CQRS? (subsection from source — full topic)
.u
When we segregate read and write concerns in two different databases, the primary
its
consideration is to sync these two databases properly — both should stay aligned.
This can be achieved using Event Driven Architecture. When an update command is issued
@
to the write database, it will publish an update event using message broker systems; this is
consumed by the read side, which pulls the latest changes to keep the read database in sync.
This option creates a consistency characteristic: data would not be reflected immediately
because async communication is used — this follows eventual consistency.
Eventual consistency (as in the source): a property of distributed computing
systems such that the value for a specific data item will, given enough time without
updates, be consistent across all nodes. The read database eventually
synchronizes with the write database (sync takes time).
When you start on the design, you can initially create a read database from replicas of the
write database. With separate read/write databases, both scale independently.
an
Interview stripIn one line: Split command model vs query model; sync via events → eventual
m
read freshness.
Say: “We trade immediate read-after-write everywhere for scalable reads and clearer
.a
ownership.”
ed
3. Event sourcing
er
With CQRS, reads and writes are separated and must be kept in sync (often via Event-Driven
ilt
Architecture). But any failure in this sync or any loss of event can make data inconsistent —
to overcome this, Event Sourcing is used.
nf
In Event Sourcing, apart from publishing events on message brokers, we store the events in
.u
the write database, and this becomes a single source of truth. In case of failure, events can
be replayed to help keep data consistent.
its
User table example (from source)
@
Suppose there is a user’s table and a user updates their details. Usually, updated details
override existing values — most applications store the entity’s current state.
With Event Sourcing, instead of storing just the latest state, we store all operations in the
database. The pattern suggests saving all events with a sequential order — this events
database is called an event store.
The event store is the single source of truth. These stores are converted to a read database
using materialized views. Conversion can also use publish/subscribe with events through
message brokers. The event list allows replay at a given timestamp — we can rebuild the
latest status after failure.
Shopping cart use case (CQRS + Event Sourcing) — text + figure from
source
an
For the shopping cart in e-commerce with CQRS and Event Sourcing: every user’s actions are
recorded in the event store with appended events. Events are combined and summarized on
m
the read database with denormalized tables into a materialized view database.
By applying these patterns, we can query the latest status of the shopping cart using
.a
materialized views. Instead of storing only “actual data,” we store sequential events denoting
user actions — we know the history with timestamps, and can retrieve cart status at any point
in time. ed
Technologies mentioned in source for event store write databases: Azure Cosmos DB,
er
Cassandra, Event Store databases, etc.
ilt
nf
.u
its
@
an
m
.a
ed
er
Interview stripPair concepts: CQRS splits read/write; Event Sourcing stores history — often
ilt
together, not always.
Cost: schema evolution, operational complexity, projection discipline.
nf
.u
4. SAGA
its
Usually, Microservices are designed around business domains, so the application is divided
@
into multiple Microservices. The challenge: any transaction is now distributed across services.
The Saga pattern addresses this.
The Saga design pattern is used for managing data consistency in distributed transactions.
The Saga pattern creates a set of transactions that update each Microservice sequentially and
publish an event to trigger the following transaction for the next Microservices. If the transaction
fails at any step, the Saga triggers rollback — reverse operations in each Microservice.
Formal definition (source): A saga is a sequence of local transactions. Each
local transaction updates the database and publishes a message or event to
trigger the next local transaction in the saga. If a local transaction fails because it
violates a business rule, the saga executes a series of compensating
transactions that undo the changes made by the preceding local transactions.
Two options for how Saga is implemented (from source):
an
● Choreography — Each local transaction publishes domain events that trigger local
transactions in other services
● Orchestration — An orchestrator (object) tells the participants what local transactions
m
to execute
.a
Choreography-based saga (full narrative from source)
ed
Choreography implements the saga using publish-subscribe. Each Microservice executes its
own local transactions and publishes events to a message broker that triggers local
transactions in other Microservices.
er
E-commerce flow described in source:
ilt
1. Order service receives POST for an order (step 1), updates its local DB
2. Publishes “order created” on the order event channel
nf
3. Customer service subscribes, receives notification, runs local tasks: check credits,
reserve credits for that order
.u
4. Then emits “credit reserved” or “credit limit exceeded” on the customer event
its
channel, subscribed by order service — completing the distributed transaction
For fewer services, this works well. When more services join, complexity grows: each service
@
must emit and consume events in addition to local tasks — orchestration helps.
an
m
.a
ed
er
ilt
nf
Orchestration-based saga (full narrative from source)
.u
The orchestrator-based saga also operates on events, but responsibility for emitting events
and ensuring atomicity of transactions shifts from services to the orchestrator. The
its
orchestrator commands services to execute local transactions and maintains the status of the
complete transaction.
@
Flow from source:
1. Order service receives POST for placing an order (step 1)
2. Order service initiates a transaction and creates a saga orchestrator
3. Orchestrator asks order service to create order in pending state
4. Orchestrator sends “Reserve credit” to customer service
5. Customer service reserves credit and sends status on its local transaction channel;
orchestrator consumes it
6. If positive → orchestrator asks order service to move order pending → In-Progress
7. If credit not reserved → roll back entire transaction from order and customer service
With the orchestrator emitting events, this suits complex workflows with many services and
future additions, with less cyclical dependency risk.
an
Use the Saga pattern when you need to (from source):
● Ensure data consistency in a distributed system without tight coupling
m
● Roll back or compensate if one of the operations in the sequence fails
Interview stripChoreography vs orchestration: decentralized events vs central coordinator
.a
— trade visibility vs coupling to the orchestrator.
Never say: “Global ACID across microservice DBs.” Say local ACID + saga + eventual
consistency. ed
er
5. BFF (Backend for Frontend)
ilt
nf
When an application is designed in Microservices, the frontend (web or mobile) may need data
from multiple services. Then it becomes the frontend team’s responsibility to transform
.u
and aggregate responses.
Challenges (from source):
its
● Frontend must handle many transformations and aggregations
@
● Browser/mobile uses more resources for rendering the page
BFF introduces an intermediate layer between frontend and backend. The BFF acts as a
proxy that calls backend services, aggregates and transforms responses per frontend needs,
and exposes ready-to-consume responses — minimal logic on the frontend. BFF
streamlines data formatting and provides a well-focused response.
an
m
.a
ed
er
ilt
nf
.u
Ownership (source): Ideally the front-end team manages the BFF. A single BFF is focused
its
on one UI only — keeps frontends simple and gives a unified view through its backend.
When BFF is not needed (source): Simple applications migrating to Microservices where each
@
service has its own UI.
When BFF fits best (source): Complex functionality with multiple third-party integrations;
screens need processed data from multiple services; client needs optimal rendering with
complex Microservices.
Multiple BFFs (source): Same backend serves web and mobile with different presentations →
one BFF for web, one for mobile.
Closing caution (source): Implement patterns judiciously; avoid code duplication.
Interview stripGateway vs BFF: Gateway = edge (auth, routing, rate limits); BFF = per-client
aggregation. Often both exist together.
6. API Gateway
an
For a large, complex Microservices application with multiple clients, use the API gateway
pattern. It applies to distributed systems and acts as a reverse proxy / gateway routing for
m
client requests. The API gateway is the communication layer between client and services —
single entry point while abstracting internal architecture. It provides cross-cutting concerns
.a
like authentication, SSL termination, and caching.
ed
Cautions (from source):
● A single node gateway risks single point of failure — must be handled
er
● Too much complex logic in the gateway becomes an anti-pattern
● As usage grows, deploy multiple API Gateways for multiple services based on use
ilt
case and count of services
● In summary: be cautious about one API Gateway for all internal microservices —
nf
decide based on business context
.u
its
@
7. Strangler pattern
The Strangler Pattern is a methodology to gradually migrate a Monolithic application to
Microservices when the app is too large to migrate simultaneously.
New Microservices gradually take over monolith functionality. Initially requests are split
an
between monolith and new service for the same functionality. Over time, microservices handle
more traffic, monolith less, until the monolith is replaced.
m
Basic steps (complete list from source)
.a
1. Identify monolith functionality around the business domain convertible to a
Microservice
ed
2. Create new Microservices from scratch for that functionality
3. Configure partial traffic to new services via API gateway or proxy
er
4. Gradually increase traffic to Microservices while decreasing monolith traffic
5. Monitor performance of new Microservices and adjust traffic
6. Eventually route entire traffic to Microservices and bring down the monolith
ilt
nf
.u
its
@
an
m
.a
ed
er
ilt
nf
.u
8. Circuit breaker
its
The Circuit Breaker Pattern improves resilience and reliability of distributed systems. Wrap a
function call in a circuit breaker object that monitors failures. Once failures reach a threshold,
@
the breaker trips — further calls return an error and calls are skipped. Can be configured to
return a default value or cached response.
Basic steps (complete list from source)
1. Identify Microservices that need to communicate
2. Add a Circuit Breaker between them
3. Configure monitoring to detect failures
4. On failure to respond, prevent further requests and handle failure appropriately
5. Optionally configure default response for graceful degradation
an
m
.a
ed
er
ilt
nf
.u
its
Interview strip Pair with timeouts, retries + jitter, bulkheads — breaker alone is not a full
resilience story.
@
9. Externalized configuration
Microservices run in containers, deployed and scaled independently — many instances of
the same service may run at once.
Scenario from source: Change configuration for a microservice replicated ~100 times. If config
is packaged with the service, you must redeploy each instance → risk of inconsistent state
(some instances still on old config). Therefore services should share external configuration.
Mechanism (source): Configurations live in an external store — database, file system, or
environment variables. On deploy/start, the service loads config from the external source. At
runtime, configuration changes are generally reloaded without new deployments.
an
m
.a
ed
er
ilt
nf
.u
its
10. Consumer-Driven Contract Tracing
@
Naming note: The attached PDF uses the title “Consumer-Driven Contract Tracing”, but the
body describes Consumer-Driven Contract Testing (CDCT) — not distributed trace IDs.
Below preserves all source wording where quoted, and uses CDCT as the accurate technical
term.
This pattern is described as the test-driven development pattern for Microservices — a
design-first approach where negotiation on the expected response between provider and
consumer decides the outcome.
Consumer service developers write “contracts” specifying responses they expect from the
provider. It is consumer-driven because consumer developers drive the contract and lead
negotiations with provider developers.
an
The contract is typically JSON or XML accessible to both sides.
Benefits (from source):
m
● Collaboration from the start between providers and consumers
● Providers understand consumer requirements proactively
.a
● Helps prevent integration issues; defining the contract up front saves many testing
cycles
ed
Steps to implement CDCT (complete numbered list from source)
er
1. Define the contract: Consumer defines what the provider must follow — expected
input and output, plus other requirements
ilt
2. Implement the contract: Provider implements and ensures the service meets
requirements
nf
3. Run tests: Provider runs unit and integration tests to verify compliance
4. Publish the contract: After implementation and passing tests, publish the contract to
.u
the consumer
its
5. Verify compatibility: Consumers verify they got what was agreed upon
@
an
m
.a
Tooling In practice teams often use Pact or similar frameworks — the whitepaper stays
ed
tool-agnostic.
er
Conclusion
ilt
With the increase in usage of Microservices-based architecture, complexities arise in
nf
managing scalability or handling distributed services' transactions. However, a set of
defined patterns, tested repeatedly, give solutions to problems common in Microservice-based
.u
architectures. Knowing each pattern provides insight into how Microservices architecture
handles performance, scalability, agility, and maintainability. We hope the patterns
its
described above provide good insight for you.
@