Back to All Concepts
ArchitectureDistributed SystemsDesign PatternsIntermediate

Microservices Architecture

An architectural style that structures an application as a collection of loosely coupled, independently deployable services.

What Are Microservices?

Microservices is an architectural approach where an application is built as a suite of small, independent services. Each service runs in its own process, owns its data, and communicates via lightweight mechanisms (typically HTTP/REST or messaging).

Microservices vs. Monolith

AspectMonolithMicroservices
DeploymentDeploy entire appDeploy services independently
ScalingScale entire appScale specific services
TechnologyOne stackEach service can use different stack
Team StructureOne large teamSmall teams own services
Failure ScopeOne component fails = app failsOne service fails, others continue
ComplexitySimpler initiallyComplex distributed system

Key Characteristics

1. Single Responsibility

Each service does ONE thing well.

❌ Monolith: OrderService handles orders, payments, inventory, notifications
✅ Microservices: 
   - OrderService → manages orders
   - PaymentService → handles payments
   - InventoryService → tracks stock
   - NotificationService → sends emails/SMS
Click to expand code...

2. Independent Deployment

Update and deploy services without touching others.

3. Decentralized Data Management

Each service owns its database. No shared databases.

4. Smart Endpoints, Dumb Pipes

Business logic lives in services, not in the communication layer.

5. Designed for Failure

Services assume other services can fail. Implement retries, fallbacks, circuit breakers.

How Services Communicate

Synchronous (Request/Response)

REST APIs

Order Service → HTTP GET /users/123 → User Service
             ← { "name": "John", "email": "..." }
Click to expand code...

gRPC Faster than REST, uses Protocol Buffers. Better for internal service-to-service calls.

Asynchronous (Event-Driven)

Message Queues

Order Service → [order_created event] → Queue → Notification Service
                                              → Inventory Service
                                              → Analytics Service
Click to expand code...

Pros: Services are decoupled, resilient to failures Cons: Eventual consistency, harder to debug

Service Discovery

How do services find each other in a dynamic environment?

Client-Side Discovery

Client queries a registry, then calls the service directly.

Order Service → Registry: "Where is User Service?"
             ← "http://10.0.0.5:8080"
Order Service → http://10.0.0.5:8080/users/123
Click to expand code...

Tools: Netflix Eureka, Consul

Server-Side Discovery

Client calls a load balancer, which routes to services.

Order Service → Load Balancer → User Service (instance 1, 2, or 3)
Click to expand code...

Tools: AWS ALB, Kubernetes Services

API Gateway

A single entry point for all client requests.

Mobile App ─┐
Web App ────┼→ [API Gateway] → User Service
Partner API ┘                → Order Service
                             → Payment Service
Click to expand code...

Responsibilities

  • Routing requests to appropriate services
  • Authentication/Authorization
  • Rate limiting
  • Request/response transformation
  • Caching
  • SSL termination

Tools: Kong, AWS API Gateway, Nginx, Envoy

Data Management Patterns

Database per Service

Each service has its own database. This is the golden rule.

User Service → PostgreSQL (users)
Order Service → MongoDB (orders)  
Product Service → Elasticsearch (products)
Click to expand code...

Challenge: How to query across services?

Saga Pattern

Manage distributed transactions as a sequence of local transactions.

1. Order Service: Create order (pending)
2. Payment Service: Charge card
   - Success → continue
   - Failure → compensate (cancel order)
3. Inventory Service: Reserve stock
4. Order Service: Mark order as confirmed
Click to expand code...

CQRS (Command Query Responsibility Segregation)

Separate read and write models for complex domains.

Resilience Patterns

Circuit Breaker

Stop calling a failing service to prevent cascade failures.

Normal: Order Service → User Service (works)
Failures build up: Order Service → User Service (timeout, timeout, timeout)
Circuit OPEN: Order Service → [Circuit Breaker] → Return fallback immediately
After cooldown: Order Service → [Circuit Breaker] → Try User Service again
Click to expand code...

Tools: Netflix Hystrix, Resilience4j, Polly

Retry with Backoff

Retry failed requests with increasing delays.

python
delays = [100ms, 200ms, 400ms, 800ms, 1600ms]  # Exponential backoff
Click to expand code...

Bulkhead

Isolate service calls so one failing integration doesn't exhaust all threads.

Timeout

Always set timeouts. Never wait forever.

Observability

In microservices, understanding system behavior is hard. You need:

1. Centralized Logging

Aggregate logs from all services. Tools: ELK Stack, Splunk, Datadog

2. Distributed Tracing

Track a request across services. Tools: Jaeger, Zipkin, AWS X-Ray

3. Metrics & Monitoring

Track latency, error rates, throughput. Tools: Prometheus + Grafana, Datadog

4. Alerting

Get notified when things break.

Real-World Examples

Netflix

Hundreds of microservices. Pioneer of many patterns (Eureka, Hystrix, Zuul). Each team owns their services end-to-end.

Uber

Started monolith, migrated to microservices. Services for rides, payments, maps, pricing, notifications. Uses gRPC internally.

Amazon

"Two-pizza teams" each own a service. Services communicate via REST and queues. Led to creation of AWS.

When NOT to Use Microservices

Small team / early-stage startup: Overhead isn't worth it ❌ Simple domain: If a monolith fits, use it ❌ Inexperienced team: Distributed systems are hard ❌ Tightly coupled domains: If everything talks to everything

Start with a modular monolith. Extract services when you have clear team/scaling boundaries.

Migration Strategy: Strangler Fig

Gradually replace parts of a monolith with microservices.

Phase 1: Monolith handles everything
Phase 2: New feature as microservice, monolith calls it
Phase 3: Extract another piece
Phase 4: Monolith is hollowed out, eventually retired
Click to expand code...

Interview Tips 💡

When discussing microservices in system design:

  1. Don't default to microservices: "We'd start with a monolith and extract services as the team grows..."
  2. Justify service boundaries: "We'd have separate services for payments and orders because they have different scaling needs..."
  3. Discuss communication: "Services would communicate via async events for loose coupling..."
  4. Address data consistency: "For distributed transactions, we'd use the Saga pattern..."
  5. Mention observability: "We'd need distributed tracing to debug requests spanning services..."

Related Concepts

About ScaleWiki

ScaleWiki is an interactive educational platform dedicated to demystifying distributed systems, software architecture, and system design. Our mission is to provide high-quality, technically accurate resources for software engineers preparing for interviews or solving complex scaling challenges in production.

Read more about our Editorial Guidelines & Authorship.

Educational Disclaimer: The architectural patterns and system designs discussed in this article are based on common industry practices, technical whitepapers, and public engineering blogs. Actual implementations in enterprise environments may vary significantly based on specific product requirements, legacy constraints, and evolving technologies.

Related Articles