Backend Development 34 min read

Microservice Architecture and Its Most Important Design Patterns

This article introduces microservice architecture, explains its advantages and disadvantages, and details ten essential design patterns—including Database per Service, Event Sourcing, CQRS, Saga, BFF, API Gateway, Strangler, Circuit Breaker, Externalized Configuration, and Consumer‑Driven Contract Testing—while providing guidance on when to apply each pattern and offering technology examples.

Top Architect
Top Architect
Top Architect
Microservice Architecture and Its Most Important Design Patterns

Microservice Architecture

Since the 1960s, software engineers have struggled with the complexity of large systems. Traditional techniques such as modularization (Parnas, 1972), encapsulation (Dijkstra, 1974), and SOA (1988) used a divide‑and‑conquer approach, but they no longer suffice for modern web‑scale applications. Microservices continue the divide‑and‑conquer philosophy with a new implementation.

Software design patterns provide reusable solutions to common problems, enabling shared vocabulary and avoiding reinventing the wheel.

What You Will Learn

Microservice architecture

Advantages of microservices

Disadvantages of microservices

When to use microservices

The most important microservice design patterns, their pros and cons, use cases, context, technology‑stack examples, and resources are described below.

Microservice Architecture Overview

Microservices split a large, complex system into vertically sliced, independently deployable processes that communicate via lightweight, language‑agnostic synchronous (e.g., REST, gRPC) or asynchronous (message) network calls.

Key Characteristics of Microservice Architecture

The application is divided into independent subprocesses, each containing multiple internal modules.

Unlike modular monoliths or SOA, services are split vertically by business domain.

Boundaries are external; services communicate over the network.

Each service runs as an independent process and can be deployed independently.

Communication is lightweight, without a heavyweight integration layer.

Advantages

Better development scalability

Faster development speed

Supports iterative or incremental development

Leverages modern ecosystems (cloud, containers, DevOps, Serverless)

Enables horizontal and fine‑grained scaling

Smaller codebases reduce cognitive load

Disadvantages

More active components (services, databases, processes, containers, frameworks)

Complexity shifts from code to infrastructure

Increased RPC calls and network traffic

System‑wide security management becomes harder

Overall system design becomes more difficult

Introduces distributed‑system complexities

When to Use Microservices

Large‑scale web applications

Enterprise projects with multiple development teams

Long‑term value outweighs short‑term speed

Team includes senior architects or engineers capable of designing microservices

Microservice Design Patterns

Database per Microservice

Replacing a monolith with microservices forces a critical decision about data storage. Keeping a single central database is a common anti‑pattern that creates tight coupling. The recommended approach is to give each service its own logical data store, which may share a physical database but use separate schemas, tables, or collections.

Pros

Data is fully owned by the service

Reduces coupling between development teams

Cons

Sharing data across services becomes challenging

Ensuring ACID transactions across services is difficult

Designing the data‑split is complex

When to Use

Large enterprise applications

Teams need full control to scale development and speed

When Not to Use

Small‑scale applications

Single team owns all services

Technology Examples : Any SQL or NoSQL database that supports logical separation (separate tables, collections, or schemas).

Event Sourcing

When services have dedicated databases, they often need to exchange data. Event sourcing stores every state‑changing event instead of the current state, allowing services to reconstruct state by replaying events. This enables high scalability and fault tolerance.

Pros

Provides atomic operations for scalable systems

Automatically records history and enables time‑travel debugging

Supports loosely coupled, event‑driven microservices

Cons

Reading state from an event store adds complexity (often requires CQRS)

System complexity increases; usually needs domain‑driven design

Must handle idempotency and possible event loss

Event schema evolution is challenging

When to Use

High‑throughput transactional systems using relational or NoSQL databases

Elastic, highly scalable microservice architectures

Message‑driven systems (e‑commerce, booking, etc.)

When Not to Use

Low‑scale transactional systems on relational databases

Simple microservices where synchronous API calls suffice

Technology Examples : EventStoreDB, Apache Kafka, Confluent Cloud, AWS Kinesis, Azure Event Hub, GCP Pub/Sub, MongoDB, Cassandra, DynamoDB; frameworks such as Lagom, Akka, Spring, Axon, Eventuate.

Command‑Query Responsibility Segregation (CQRS)

When using event sourcing, reading data from the event store can be expensive. CQRS separates write (command) and read (query) models, allowing each to be optimized independently. Simple CQRS uses separate ORM models; advanced CQRS may use distinct data stores for reads and writes.

Pros

Improves read performance in event‑driven microservices

Provides high availability of data

Read and write sides can scale independently

Cons

Read store is eventually consistent

Overall system complexity rises; misuse can jeopardize projects

When to Use

High‑scale microservices that already use event sourcing

Complex domain models with divergent read/write requirements

Systems where read and write loads differ significantly

When Not to Use

Small, stable services where a single data model suffices

When the overhead of maintaining two models outweighs benefits

Technology Examples : Write stores – EventStoreDB, Kafka, Kinesis, etc.; Read stores – Elasticsearch, Solr, Cloud Spanner, Aurora; Frameworks – Lagom, Akka, Spring, Axon, Eventuate.

Saga

Distributed transactions are hard in microservices, especially with independent databases. The Saga pattern breaks a global transaction into a series of local transactions, each followed by an event or message. If a local transaction fails, compensating transactions roll back previous work.

Pros

Provides consistent transactions for scalable, event‑driven microservices

Works with non‑relational databases that lack 2PC support

Cons

Requires handling of instant failures and idempotency

Debugging is difficult; complexity grows with service count

When to Use

High‑scale, loosely coupled microservices that use event sourcing

Systems built on distributed NoSQL databases

When Not to Use

Low‑scale transactional systems on relational databases

Systems with circular service dependencies

Technology Examples : Axon, Eventuate, Narayana.

Backend‑for‑Frontend (BFF)

When a product has both web and mobile clients, their differing requirements (screen size, performance, bandwidth) often necessitate separate APIs. A BFF provides a tailored backend for each UI, encapsulating downstream services and improving security.

Pros

Separates concerns, allowing UI‑specific optimizations

Improves security

Reduces chatter between UI and downstream services

Cons

Potential code duplication across BFFs

Many BFFs increase maintenance effort

Should contain only UI‑specific logic, not business logic

When to Use

Multiple UIs with distinct API needs

Security requirements demand an extra layer

Micro‑frontend architectures

When Not to Use

Multiple UIs share the same API

Core services are not deployed in a DMZ

Technology Examples : Any backend framework (Node.js, Spring, Django, Laravel, Flask, Play, …).

API Gateway

In microservice environments, clients may need to call many fine‑grained services, leading to complexity. An API gateway acts as a façade, routing requests, aggregating responses, and handling cross‑cutting concerns such as SSL termination, authentication, rate limiting, and logging.

Pros

Provides loose coupling between front‑end and back‑end

Reduces number of client‑service calls

Enables centralized security (SSL, auth, authz)

Centralizes cross‑cutting concerns (logging, monitoring, throttling, load balancing)

Cons

Potential single point of failure

Additional network hop adds latency

Can become a bottleneck if not scaled

Increases operational cost

When to Use

Complex microservice landscapes where a gateway is almost mandatory

Large enterprises needing centralized security and cross‑cutting management

When Not to Use

Small projects or startups where security and centralization are not priorities

Very few microservices

Technology Examples : Amazon API Gateway, Azure API Management, Apigee, Kong, WSO2 API Manager.

Strangler

To migrate a legacy monolith to microservices, the Strangler pattern incrementally replaces monolith functionality with new services, routing traffic via a façade (often an API gateway). Once all functionality is migrated, the monolith is retired.

Pros

Safe migration of monolith to microservices

Parallel development of new features and migration of existing ones

Controlled migration pace

Cons

Sharing data stores between monolith and new services is challenging

Introducing a façade adds latency

End‑to‑end testing becomes more difficult

When to Use

Incrementally moving a large backend monolith to microservices

When Not to Use

Small monoliths where a full rewrite is simpler

Inability to intercept client traffic to the legacy system

Technology Examples : Any API‑gateway‑backed framework.

Circuit Breaker

Microservices that call each other synchronously can suffer cascading failures when a downstream service becomes unavailable. A circuit breaker monitors failure rates and short‑circuits calls when a threshold is exceeded, returning errors quickly.

States

Closed : Requests flow normally; failures are counted.

Open : Requests fail fast; after a timeout the breaker moves to half‑open.

Half‑Open : A limited number of requests are allowed; success closes the breaker, failure re‑opens it.

Pros

Improves fault tolerance and resilience

Prevents cascading failures

Cons

Requires sophisticated error handling

Needs monitoring and logging

Should support manual reset

When to Use

Synchronous, tightly coupled microservice calls

Services depend on multiple other services

When Not to Use

Loosely coupled, event‑driven architectures

Services that do not call others

Technology Examples : API gateways, service meshes, libraries such as Hystrix, Resilience4j, Polly.

Externalized Configuration

Hard‑coding configuration (database URLs, credentials, etc.) in code is a security risk and forces rebuilds for every change. Externalizing configuration separates build from runtime, keeping secrets out of the codebase and allowing dynamic updates.

Pros

Reduces security exposure

No rebuild needed for config changes

Cons

Requires a framework that supports externalized config

When to Use

Any production‑grade application

When Not to Use

Proof‑of‑concept or throw‑away prototypes

Technology Examples : All modern enterprise frameworks (Spring Boot, Micronaut, Quarkus, etc.).

Consumer‑Driven Contract Testing

Microservices built by different teams need reliable integration. Consumer‑driven contract tests let the consumer define expectations (requests and responses). Providers run these contracts as part of their CI pipeline, ensuring compatibility.

Pros

Detects breaking changes quickly

Reduces unexpected failures in large systems

Improves team autonomy

Cons

Additional effort to create and maintain contracts

If contracts diverge from reality, production failures may occur

When to Use

Large enterprise applications with many independently developed services

When Not to Use

Small applications where a single team owns all services

Stable services with little change

Technology Examples : Pact, Postman, Spring Cloud Contract.

Conclusion

Microservice architecture enables scalable development for large enterprise software, but it is not a universal silver bullet. Teams should adopt proven design patterns—especially Database per Service, Event Sourcing, CQRS, and Saga—to handle data consistency, and use BFF or API Gateway for multi‑client scenarios. Circuit breakers improve resilience, Strangler assists migration, and consumer‑driven contracts ensure reliable integration, while externalized configuration is essential for any modern application.

The patterns presented are not exhaustive; real projects may require additional patterns, but this collection offers a solid foundation for designing robust microservice systems.

design patternsdistributed systemsbackend architecturemicroservicesAPI Gatewaycircuit breaker
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.