Architecture Patterns

Microservices Architecture: Principles, Patterns, and Trade-offs

Learn the principles of microservices architecture, including service decomposition strategies, inter-service communication, data management, and the trade-offs involved.

24 min readmicroservicesarchitectureSOAdecompositionAPI design

Why Microservices?

Microservices architecture structures an application as a collection of small, autonomous services. Each service is self-contained, implements a single business capability, and can be deployed independently.

Historical context: Microservices evolved from service-oriented architecture (SOA). They can be seen as "SOA done right" — avoiding the complexity of enterprise service buses (ESBs) by using simple, lightweight protocols.


Monolith vs Microservices

Monolithic Architecture

All components in a single deployable unit.

Microservices Architecture

Each service runs independently with its own database.


Key Principles of Microservices

1. Single Responsibility Principle

Each service does one thing well — a single business capability.

2. Decentralized Data Management

Each service owns its database. This is the most important and controversial principle.

3. Smart Endpoints, Dumb Pipes

Services communicate via simple protocols (HTTP, message queues), not through an ESB.

4. Design for Failure

Every service can fail. Build resilience with retries, circuit breakers, and fallbacks.

5. Infrastructure Automation

Use CI/CD, container orchestration (Kubernetes), and infrastructure as code.

⚠️

Don't start with microservices: If you're building something new or small, start with a modular monolith. Split into microservices when you have a proven need (team scaling, independent deployment, technology diversity).


Service Decomposition Strategies

By Business Capability

Group services by what the business does, not by technical layers.

Business CapabilityService
Customer managementCustomer Service
Order managementOrder Service
Payment processingPayment Service
Product catalogCatalog Service
Delivery managementDelivery Service

By Subdomain (Domain-Driven Design)

Use bounded contexts from your domain model to define service boundaries.

Avoiding Common Mistakes

Anti-PatternProblemSolution
NanoservicesToo many tiny servicesCombine related functionality
Shared databaseTight coupling, deployment issuesPer-service databases
Distributed monolithPretending microservices but all deploy togetherConsolidate or truly decouple
Chatty servicesToo many network calls between servicesUse batch operations, caching, or event sourcing

Inter-Service Communication

Synchronous: REST/gRPC

python
# REST call between services
import httpx

async def get_user(user_id: str):
    async with httpx.AsyncClient() as client:
        response = await client.get(f"http://user-service/users/{user_id}")
        return response.json()

Asynchronous: Message Queues


Data Management Patterns

Database per Service

Saga Pattern (Distributed Transactions)

Instead of ACID transactions across services, use a sequence of local transactions with compensating actions.

API Composition

Combine data from multiple services at the API gateway or a dedicated BFF (Backend for Frontend).


Operational Complexity

ConcernMonolithMicroservices
DeploymentSingle deployMultiple deploys
TestingEasierComplex (integration testing)
MonitoringSimpleDistributed tracing, metrics
DebuggingStack traceCorrelate across services
Database migrationsSimpleCoordinate across services

Essential Tools for Microservices

  • Service mesh: Istio, Linkerd (mTLS, traffic management)
  • API gateway: Kong, AWS API Gateway (routing, auth, rate limiting)
  • Distributed tracing: Jaeger, Zipkin, AWS X-Ray
  • Container orchestration: Kubernetes, Docker Swarm
  • Service discovery: Consul, Eureka, Kubernetes DNS

Start with these minimums:

  1. Containerization (Docker)
  2. Orchestration (Kubernetes or managed k8s)
  3. Centralized logging
  4. Distributed tracing
  5. Health checks and graceful shutdown

When to Use Microservices

Use CaseRecommendation
Small team, new projectMod monolith
Large team, complex domainMicroservices
Need independent scalingMicroservices
Different tech stacks neededMicroservices
Fast iteration requiredMicroservices
Simple CRUD appMonolith

What to Remember for Interviews

  1. Core principle: Each service owns its data; never share databases
  2. Trade-offs: More operational complexity for independent deployment and scaling
  3. Start simple: Don't start with microservices unless you have a proven need
  4. Communication: Use synchronous (REST, gRPC) for request-response, async (events) for decoupled flows
  5. Data consistency: Use sagas for distributed transactions, eventual consistency is often acceptable

Practice: Draw a microservices architecture for a familiar application (e.g., Netflix, Uber, Twitter). Identify the services, their boundaries, and how they communicate.