Skip to content

🔧 Level 6 — Microservices Architecture

Breaking monoliths apart — and the trade-offs that come with it.

Microservices architecture splits a large application into small, independent services. Each service owns one business capability, runs in its own process, and can be deployed, scaled, and maintained on its own.

Think of it like a restaurant kitchen: instead of one chef doing everything (monolith), you have specialists — grill, pastry, drinks — each with their own station and tools, coordinated by a head chef (API Gateway).


📚 Table of Contents

  1. What Are Microservices?
  2. Monolith vs Microservices
  3. Anatomy of a Microservices System
  4. API Gateway
  5. How Services Communicate
  6. Real-World Example: E-Commerce Order Flow
  7. Service Discovery
  8. Circuit Breaker Pattern
  9. Data Management — Database per Service
  10. Service Mesh (Istio / Linkerd)
  11. When to Use (and When Not To)
  12. Trade-offs — The Honest Picture
  13. Checklist Before Moving On

6.1 What Are Microservices?

A monolith is one big deployable unit:

  • One codebase
  • One database (usually)
  • One deployment

A microservices system is many small deployable units:

  • One service per business domain (Auth, Orders, Payments, etc.)
  • Database per service — each team owns its data
  • Independent deployments and scaling

TIP

Rule of thumb: Start with a monolith. Move to microservices when team size, scale, or deployment pain justify the operational cost. Interviewers love hearing this — it shows maturity, not hype.


6.2 Monolith vs Microservices

Architecture Comparison

MonolithMicroservices
Simplicity✅ Simple❌ Complex
Scaling❌ Scale all✅ Scale specific services
Deployment✅ One deploy❌ Many deploys
Tech diversity❌ One stack✅ Best tool per service
Failure isolation❌ One bug = all down✅ Isolated failures
Team ownership❌ Shared codebase✅ Clear ownership

The Trade-off in One Line

text
Monolith:       Simple to develop, test, and deploy at small scale.
                Painful at large scale (tight coupling, team bottlenecks).

Microservices:  Complex to operate (networking, discovery, observability).
                Essential at FAANG scale (hundreds of services, thousands of engineers).

6.3 Anatomy of a Microservices System

Here is the full picture of how the pieces fit together. Clients never call microservices directly — they go through the API Gateway.

Key Building Blocks

ComponentRole
API GatewaySingle entry point; handles auth, rate limiting, routing
Service DiscoveryServices find each other dynamically (Consul, Eureka, K8s DNS)
Message QueueAsync communication (Kafka, RabbitMQ, SQS)
Circuit BreakerStops cascading failures when a service is down
Service MeshHandles networking (mTLS, retries, tracing) via sidecar proxies
Database per ServiceEach service owns its data — no shared tables across services

6.4 API Gateway

The API Gateway is the single entry point for all client requests. It handles cross-cutting concerns so individual services stay focused on business logic.

What an API Gateway Does

Request Flow

API Gateway vs Load Balancer

FeatureLoad BalancerAPI Gateway
PurposeDistribute traffic across instancesSmart routing + cross-cutting concerns
Auth❌ No✅ Yes
Rate limiting❌ No✅ Yes
Protocol translation❌ No✅ Yes (REST ↔ gRPC)
RoutingBy IP/portBy URL path, method, header

Gateway Routing (JavaScript Example)

Using express and http-proxy-middleware:

javascript
const express = require("express");
const { createProxyMiddleware } = require("http-proxy-middleware");
const app = express();

// Log every request
app.use((req, res, next) => {
  console.log(`[GATEWAY] ${req.method} ${req.path}`);
  next();
});

// Route paths to the right microservice
app.use("/auth", createProxyMiddleware({ target: "http://auth-service:3001" }));
app.use(
  "/orders",
  createProxyMiddleware({ target: "http://order-service:3002" })
);
app.use(
  "/payments",
  createProxyMiddleware({ target: "http://payment-service:3003" })
);

app.listen(8080);

6.5 How Services Communicate

Services talk to each other in two main ways: synchronously (wait for a response) or asynchronously (fire and forget).

Communication Styles

Synchronous — REST vs gRPC

REST + JSONgRPC + Protobuf
Best forPublic APIs, browsersInternal service-to-service
SpeedGoodFaster (binary, HTTP/2)
DebuggingEasy (human-readable)Harder (needs tooling)
ContractImplicitStrict .proto schema

INFO

For a deep dive on when to pick gRPC over REST, see gRPC — When & Why to Use It.

Asynchronous — Events & Message Queues

The caller publishes an event and moves on. Other services react when ready.

Good for: notifications, analytics, search indexing — anything that does not need an immediate response.

Trade-off: You get eventual consistency instead of instant consistency. That is usually fine for emails and dashboards, but not for payment confirmation.

TIP

Level 5 covers messaging in depth: Messaging & Event-Driven Architecture.


6.6 Real-World Example: E-Commerce Order Flow

Imagine a marketplace like Amazon, Shopify, or Daraz. Here is how microservices handle "User places an order" — a flow everyone can relate to.

Service Breakdown

ServiceResponsibilityDatabase
ProductCatalog, searchPostgreSQL + Elasticsearch
UserAccounts, addressesPostgreSQL
OrderOrder lifecyclePostgreSQL
InventoryStock levelsPostgreSQL + Redis
PaymentCard, wallet, refundsPostgreSQL
NotificationEmail, SMS, pushNone (stateless)

High-Level Architecture

Step-by-Step: Placing an Order

Example API Request

http
POST /api/v1/orders
Authorization: Bearer <jwt>
json
{
  "items": [{ "productId": "P-101", "quantity": 2, "price": 1200 }],
  "shippingAddressId": "ADDR-55",
  "paymentMethod": "card",
  "idempotencyKey": "uuid-client-generated-key"
}

IMPORTANT

The idempotencyKey prevents double-charging. If the mobile app retries after a timeout, the server recognizes the duplicate key and returns the original response instead of creating a second order.

Why Microservices Here?

ProblemMicroservices Solution
Product browsing is read-heavyScale Product + cache separately
Payments need strict isolationOwn service + own database
Flash sales spike orders 10×Scale Order + Inventory only
Notifications can lag slightlyAsync via Kafka — no blocking

TIP

Want the full production case study with capacity math, saga patterns, and deployment? See E-Commerce Microservices Case Study.


6.7 Service Discovery

In the cloud, service IP addresses change constantly — containers restart, auto-scaling adds new instances. Service Discovery lets services find each other without hard-coded URLs.

Common tools: Consul, Eureka, etcd. Kubernetes does this automatically via DNS: inventory-service.default.svc.cluster.local.

Client-Side Discovery (JavaScript Example)

javascript
async function callOrderService() {
  // 1. Fetch available instances from registry (e.g. Consul)
  const instances = await serviceRegistry.get("order-service");
  // Example: ["10.0.0.1:3001", "10.0.0.2:3001"]

  // 2. Pick one instance (simple random load balancing)
  const target = instances[Math.floor(Math.random() * instances.length)];

  // 3. Make the call
  return fetch(`http://${target}/orders`);
}

6.8 Circuit Breaker Pattern

If the Payment Service is slow or down, the Order Service should not keep hammering it and drag the whole system down. The Circuit Breaker acts like an electrical fuse — it trips open when failures exceed a threshold.

State Transitions

StateBehavior
CLOSEDNormal — requests go through
OPENService is down — return fallback immediately, no waiting
HALF_OPENTry one test request to see if the service recovered

Logic (JavaScript Example)

javascript
class CircuitBreaker {
  constructor(requestFunc) {
    this.requestFunc = requestFunc;
    this.state = "CLOSED";
    this.failures = 0;
  }

  async exec() {
    if (this.state === "OPEN") {
      return { fallback: true, message: "Payment service unavailable" };
    }

    try {
      const data = await this.requestFunc();
      this.failures = 0;
      this.state = "CLOSED";
      return data;
    } catch (err) {
      this.failures++;
      if (this.failures > 3) {
        this.state = "OPEN";
        setTimeout(() => (this.state = "HALF_OPEN"), 5000);
      }
      throw err;
    }
  }
}

TIP

More on circuit breakers in production: Circuit Breakers Pattern.


6.9 Data Management — Database per Service

Each microservice owns its data. No direct SQL joins across services — if Order Service needs stock info, it calls Inventory Service's API.

The Hard Part: No ACID Across Services

In a monolith, one database transaction can update orders and inventory atomically. In microservices, that is gone. You need patterns to stay consistent.

PatternUse Case
SagaMulti-step workflows with compensating rollback steps
Event SourcingRebuild state from an immutable event log
Idempotency keysSafe retries on payment and order APIs
2PC (Two-Phase Commit)Rare — too slow and brittle for microservices

Saga Example — Order Fails After Payment Declined

text
Step 1: Reserve inventory     ✅ success
Step 2: Charge payment      ❌ fails
Step 3: Compensate          → Release inventory (rollback)

WARNING

Never share one database between two microservices. It creates hidden coupling — two teams editing the same tables, schema conflicts, and you lose independent deployment.


6.10 Service Mesh (Istio / Linkerd)

At large scale (Netflix, Uber), networking logic moves into sidecar proxies so application code stays clean. A service mesh handles encryption, retries, and tracing at the infrastructure level.

Sidecars handle:

  • Mutual TLS encryption between services
  • Automatic retries and timeouts
  • Distributed tracing (Jaeger, Zipkin)
  • Traffic splitting for canary deployments

INFO

You do not need a service mesh on day one. Most teams add it after they have 20+ services and networking code is duplicated everywhere.


6.11 When to Use (and When Not To)

✅ Use Microservices When

  • Multiple teams need to deploy independently
  • Different parts of the system have very different scale profiles (search vs payments)
  • You need fault isolation for critical paths
  • You want polyglot services — different languages per domain

❌ Avoid Microservices When

  • Team is small (< 10 engineers)
  • Product is still finding product-market fit
  • You lack strong DevOps / observability practices
  • Domain boundaries are unclear

Quick Decision Guide

text
Small startup (5 engineers):      Monolith is usually better
Mid-size (20–50 engineers):         Start splitting hot domains
FAANG scale (1000+ engineers):        Microservices are essential

6.12 Trade-offs — The Honest Picture

Benefits

  • Independent scaling — scale Payment during sales, not Email
  • Independent deployment — ship Order Service without touching User Service
  • Team autonomy — each team owns one service end-to-end
  • Technology freedom — Go for payments, Node for API, Python for ML
  • Fault isolation — Email down does not break checkout

Costs

  • Distributed complexity — network failures, latency, partial failures
  • Data consistency — no simple transactions across services
  • Observability — need tracing, centralized logging, metrics
  • DevOps overhead — CI/CD per service, Kubernetes, service mesh
  • Testing — integration tests across services are harder

✅ Checklist Before Moving On

  • [ ] I can explain what microservices are in plain language
  • [ ] I can compare monolith vs microservices with real trade-offs
  • [ ] I know what an API Gateway does (auth, routing, rate limiting)
  • [ ] I understand sync (REST/gRPC) vs async (Kafka/events) communication
  • [ ] I can walk through an e-commerce order flow step by step
  • [ ] I can describe the Circuit Breaker pattern (CLOSED → OPEN → HALF_OPEN)
  • [ ] I understand database-per-service and the Saga compensation pattern
  • [ ] I know when not to use microservices

TopicLink
gRPC for internal communicationgRPC — When & Why
Messaging & event-driven designLevel 5 — Messaging
E-commerce full case studyE-Commerce Microservices
Circuit breaker deep diveCircuit Breakers
Saga patternSaga Pattern
Interview prepMicroservices — Interview Guide

➡️ Next: Level 7 — Real-World Systems

Released under the ISC License.