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.
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.
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.
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.