ayushsalampuriya.xyzRevise

HLD · Architecture

Microservices Architecture

Decompose a system into independently deployable services with clear boundaries.

In simple words: Microservices split a large application into smaller services that own separate business capabilities. This helps large teams deploy independently, but it adds network, data, testing, and operational complexity. Example: checkout can call Payment over HTTP with a 2-second timeout and circuit breaker so a slow payment service does not hang the whole order flow.

1. Monolith vs microservices

MonolithMicroservices
Single codebase & deployMany services, independent deploy
Simple debugging, shared DBTeam autonomy, polyglot stacks
Scale entire app togetherScale hot services only
Good for early stage / small teamsGood for large orgs with clear domains

Interview line: “Start with a monolith when it meets the need. Extract a service when measured scaling needs or team boundaries justify the extra operational cost.”

2. Domain-driven service boundaries

Split by business capability, not technical layer.

Bad split: a "Database service" plus an "API service" — this creates chatty coupling without clear ownership.

3. Communication patterns

Synchronous (HTTP/gRPC)

The Order service calls the Payment service and waits for a response. This is easy to follow, but a slow Payment service can make the Order service slow too.

Mitigation: Use timeouts, bounded retries only for safe operations, and a circuit breaker.

Asynchronous (message queue)

Order placed → event to queue → Notification sends email. Loose coupling; harder to trace.

4. Circuit breaker pattern

States: Closed (normal) → Open (fail fast after N errors) → Half-open (trial request).

Example: Recommendation service down — product page loads without recommendations instead of hanging 30s.

5. Data ownership

Each service owns its database. No shared tables across services — access via API/events only.

Example violation: Order service directly JOINs the users table in Payment DB — creates tight coupling.

5b. Walkthrough — order fails when payment is down

  1. User clicks Pay → Order service calls Payment service (sync HTTP).
  2. Payment DB is slow → Order waits 30s → user sees timeout.
  3. Fix: 2s timeout + circuit breaker. After 5 failures, open circuit → fail fast with "try again".
  4. Async option: Order records a PENDING_PAYMENT state and publishes an event. Payment processes it and emits a success or failure event. Use this only if the product accepts an asynchronous checkout flow.

6. Distributed tracing & observability

One user request spans 5 services. Use correlation ID (trace ID) in headers; tools: Jaeger, Zipkin, OpenTelemetry.

Concrete example: If checkout takes four seconds, one trace can show 50 ms in Order, 3.8 seconds in Payment, and 150 ms elsewhere. The team can investigate the actual slow service instead of guessing.

7. Example: Netflix-style decomposition

  1. Monolith streaming app → extract billing, encoding, recommendations
  2. Encoding pipeline async via queue (heavy GPU workers)
  3. API gateway + BFF (backend-for-frontend) per client (TV vs mobile)
  4. Each team owns SLA for their service

8. Challenges (say these in interviews)

Quick revision

  • Split by domain, not by layer; each service owns its data.
  • Sync: HTTP/gRPC + timeout + circuit breaker.
  • Async: Events/queues for decoupling.
  • Don't microservice too early — operational tax is real.
  • Observability: Trace IDs across services mandatory.
  • Trade-off: Independent deployment adds network and operational failure modes.