High-Level Design Tutorial 0/42 lessons ~6 min read Lesson 6

    Microservices Architecture

    Microservices decompose an application into independently deployable services, each owning a bounded context, data store, and release cadence.

    Course progress0%
    Focus
    10 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Microservices decompose an application into independently deployable services, each owning a bounded context, data store, and release cadence. Netflix, Uber, and Amazon popularized the pattern for organizational scale as much as technical scale — small teams ship small services without coordinating monolith releases.

    The cost is operational complexity: network latency, partial failures, distributed tracing, contract testing, and eventual consistency across service boundaries. HLD interviews expect you to name these costs honestly while designing service boundaries, APIs, and data ownership.

    This lesson covers decomposition strategies, synchronous vs asynchronous integration, and the observability stack that makes microservices operable.

    Understanding the topic

    Key concepts

    • Each service owns its data — no shared tables across services (database per service ideal).
    • Communication: sync REST/gRPC for queries; async events for decoupled side effects.
    • API Gateway aggregates mobile-friendly responses; services stay granular internally.
    • Independent scaling: scale search service separately from checkout.
    • Failure isolation: circuit breakers prevent cascade; bulkheads limit blast radius.
    • Requires mature DevOps: CI/CD per service, container orchestration, centralized logging.
    text
    flowchart TB
    Client --> GW[API Gateway]
    GW --> U[User Service]
    GW --> O[Order Service]
    GW --> P[Payment Service]
    O --> MQ[Kafka]
    P --> MQ

    Internal architecture

    Architecture overview

    text
    flowchart TB
    Client --> GW[API Gateway]
    GW --> U[User Service]
    GW --> O[Order Service]
    GW --> P[Payment Service]
    O --> MQ[Kafka]
    P --> MQ

    Step-by-step explanation

    1. Clients → CDN/LB → API Gateway (auth, rate limit, routing).
    2. Domain services: User, Catalog, Cart, Order, Payment, Notification — each with private DB.
    3. Order service publishes OrderPlaced event to Kafka; Notification and Analytics consume.
    4. Synchronous calls only on critical path; prefer event-driven for non-blocking steps.
    5. Service mesh (optional) handles mTLS, retries, metrics between pods.
    6. Centralized identity (OAuth2/JWT), distributed tracing (OpenTelemetry), SLO dashboards per service.

    Informative example

    Order service publishes domain events — decouples payment capture from email and analytics:

    java
    @Service
    public class OrderService {
    private final OrderRepository repo;
    private final KafkaTemplate<String, OrderPlacedEvent> kafka;
    public OrderService(OrderRepository repo, KafkaTemplate<String, OrderPlacedEvent> kafka) {
    this.repo = repo;
    this.kafka = kafka;
    }
    @Transactional
    public Order create(CreateOrderRequest req) {
    Order order = repo.save(Order.from(req));
    kafka.send("order.placed", order.id(), new OrderPlacedEvent(order.id(), req.userId(), order.total()));
    return order;
    }
    }
    public record OrderPlacedEvent(String orderId, String userId, BigDecimal total) {}
    @Component
    public class NotificationConsumer {
    @KafkaListener(topics = "order.placed", groupId = "notification")
    public void onOrderPlaced(OrderPlacedEvent event) {
    // send email asynchronously — failure retries without blocking checkout
    }
    }

    State data ownership: Order DB holds orders only. Payment service owns charges; integrate via events or sagas, not cross-DB joins.

    Real-world use

    Real-world use cases

    • Global e-commerce with teams per domain (catalog, pricing, fulfillment).
    • Ride-hailing: matching, pricing, maps, payments evolve on different release trains.
    • Banking: account service isolated from PCI-scoped payment card service.
    • Social platform: feed, graph, media transcoding scale independently.

    Best practices

    • Decompose by business capability (bounded context), not technical layer.
    • Design idempotent consumers and compensating transactions for sagas.
    • Version APIs; avoid breaking consumers — contract tests in CI.
    • Limit synchronous call chains to 2–3 hops; prefer events for fan-out.
    • Standardize platform concerns: auth, logging format, health checks.
    • Start with a monolith extract — strangler fig pattern — not big-bang rewrite.

    Common mistakes

    • Shared database across microservices — creates hidden coupling.
    • Chatty fine-grained services causing latency and operational nightmares.
    • Distributed monolith: must deploy all services together due to tight coupling.
    • No correlation IDs — impossible to debug cross-service failures.
    • Ignoring data consistency — expecting ACID across services without saga design.

    Advanced interview questions

    Q1BeginnerWhat defines a microservice?
    Independently deployable service owning a bounded context and its data, communicating over network APIs or events.
    Q2BeginnerMain downside of microservices?
    Operational complexity, network failures, distributed data consistency, and higher coordination overhead.
    Q3IntermediateSync vs async between services?
    Sync for immediate query/validation; async for side effects, fan-out, and peak absorption via queues.
    Q4IntermediateHow avoid distributed monolith?
    Strict data ownership, loose coupling via events, independent deploy pipelines, and stable API contracts.
    Q5AdvancedSplit e-commerce monolith — what services first?
    Extract Payment (compliance), Notification (async), Catalog (read-heavy scale) — keep Order orchestration thin with saga; justify boundaries with team and scale drivers.

    Summary

    Microservices trade simplicity for independent scale and team velocity. Database-per-service and event-driven integration are core patterns. API Gateway and observability are non-optional platform layers. Design for failure: circuit breakers, idempotency, sagas. Extract gradually; avoid premature microservice adoption. SOA and event-driven lessons deepen integration choices next.

    Ready to mark this lesson complete?Track your journey across the entire course.