ayushsalampuriya.xyz Revise

System Design · Core

Microservices

Split a system along business boundaries: how to cut services, sync vs async links, API gateways, sagas — and when a monolith is the right call.

1. What “microservice” means here

Independently deployable services, each owning its data and a clear business capability (billing, catalog, identity). Not “one Lambda per function” and not “shared DB with many repos.”

Bad split: UserService and UserProfileService sharing users table Good split: Identity (authn) vs Orders vs Inventory, each with own store

2. Finding boundaries

Prefer a modular monolith first if the domain is unclear. Wrong cuts create distributed monoliths: network calls everywhere, one deploy still couples everyone.

3. Sync vs async between services

Synchronous (HTTP/gRPC)Asynchronous (events/queues)
WhenNeed answer now (authorize, price)Side effects, fan-out, spikes
CouplingRuntime availability couplingTemporal decoupling
FailureCascading latencyLag, eventual consistency
Checkout sync: API → Inventory.reserve() → Payments.charge() Better mix: API → Inventory.reserve() → enqueue PaymentCommand → orchestrator confirms / compensates

4. API gateway

Clients → API Gateway → Authn/Authz, rate limit, routing, TLS → Order Service → Catalog Service → ...

5. Data ownership and consistency

Database-per-service

ProsCons
Independent schema evolution and deploysNo cross-service JOINs; need APIs/events
Pick the right store per domain (SQL/KV)Duplicated data / eventual consistency
Failure isolation (one DB down ≠ all)Harder reporting; need data lake / CDC
Clear ownership and blast radiusDistributed transactions become sagas

6. Service discovery

Client-side discoveryServer-side discovery
HowClient queries registry, picks instance, calls directlyClient calls LB/DNS; LB queries registry
ProsFewer hops; client controls load-balance policySimpler clients; central policy
ConsClients need registry SDK + refresh logicLB is extra hop / dependency
ExamplesEureka + Ribbon-style; Consul clientKubernetes Service/DNS, AWS ALB/NLB, Envoy

Modern default in interviews: platform LB or service mesh (server-side / sidecar) so app code stays dumb about peer IPs.

7. Circuit breaker (states in detail)

Protect callers from a flaky dependency. Three states:

Closed (normal): calls flow; count failures in a window if failure rate > threshold → Open Open (tripped): fail fast (or fallback); do not call dependency after reset timeout → Half-Open Half-Open (probe): allow a few trial calls success → Closed; failure → Open again

8. Saga (recap in microservice context)

Long business transactions become steps with compensations. Orchestrator service or choreographed events. Expose “pending” states in the UX.

9. Operational tax

10. When NOT to use microservices

Prefer monolith / modular monolith when
Small team, unclear domain boundaries
Strong consistency across many entities in one request
You cannot yet afford platform (CI, mesh, tracing)
Latency budget cannot pay multiple network hops

Interview flex: “I would start with a modular monolith, extract services when a module needs independent scale or team ownership.” That reads as mature, not anti-microservice.

11. Strangler pattern

Old monolith <-- gateway routes /legacy/* New services <-- gateway routes /v2/* Gradually move paths; retire monolith modules

Quick revision

  • Cut by business capability and data ownership, not by layers.
  • DB-per-service: autonomy vs joins/consistency cost.
  • Discovery: client-side vs server-side/mesh — know both.
  • Circuit breaker: closed → open → half-open; always with timeouts.
  • Sync for immediate needs; async for decoupling and fan-out.
  • API gateway thin; sagas for cross-service workflows.
  • Monolith first when boundaries or platform are immature.