Microservices
Overview
Microservices architecture organizes a system as a collection of small, independently deployable services, each owning a focused domain of business logic and its own data. In system design interviews, the question of "should I decompose this?" arises in almost every advanced problem. Interviewers are not looking for a reflexive "yes", they want to see deliberate reasoning about when decomposition helps and when it adds complexity without benefit.
The monolith vs. microservice decision frames every architectural discussion that follows. Getting it right early signals that you understand both the power and the cost of distribution.
Core Concepts
Service Decomposition Strategies
The primary decomposition strategy in practice is decomposition by business domain, guided by Domain-Driven Design (DDD). Each service maps to a bounded context, a cohesive area of the business where a consistent model applies. For example, an e-commerce platform decomposes naturally into Order Management, Inventory, Payments, and Notifications. These align with team ownership, organizational structure, and independent deployment cadence.
A secondary strategy is decomposition by data ownership. Each service should own its data store; no two services share a database. This enforces loose coupling at the persistence layer and means services communicate via APIs or events rather than shared tables.
A third trigger is decomposition by scale requirement: if one component (e.g., image processing, search indexing) has radically different throughput or resource needs than the rest, extracting it as an independent service allows targeted scaling.
Service Boundaries and the Bounded Context Pattern
A service boundary is the interface contract a service exposes to the world, its API, the events it publishes, and the data schema it owns. Defining tight boundaries is the hardest part of microservice design. Common boundary violations include:
- Chatty coupling: Service A makes 5 synchronous calls to Service B per request.
- Shared database: Two services read/write the same table directly.
- God service: A single service owns too much domain logic and becomes a new monolith.
The bounded context pattern from DDD defines a conceptual boundary around a model. Within that boundary, terms like "Account" or "Order" have precise, consistent meaning. Across boundaries, the same word might mean something different, and translation happens at the anti-corruption layer.
Inter-Service Communication Overview
Services communicate either synchronously (request-response: REST, gRPC) or asynchronously (event-driven: message queues, pub/sub). The right choice depends on whether the caller needs an immediate answer and whether tight coupling is acceptable. Synchronous communication is simpler to reason about but creates latency cascades; asynchronous communication decouples services but introduces eventual consistency. These are covered in depth in the Synchronous and Asynchronous Communication learning areas.
Service Discovery and Registration
When services run as multiple instances across dynamic infrastructure (containers, cloud VMs), clients need to know where to send requests. Service discovery solves this:
- Client-side discovery: the client queries a service registry (e.g., Eureka, Consul) and selects an instance.
- Server-side discovery: the client sends to a load balancer or API gateway, which queries the registry.
Modern Kubernetes environments use built-in DNS-based service discovery where each service gets a stable DNS name regardless of pod IP changes.
Deployment Independence and Versioning
The core value proposition of microservices is independent deployment. Each team can release their service on their own cadence without coordinating with other teams. This requires:
- Backward-compatible API changes: never remove or rename fields without a deprecation window.
- Consumer-driven contract testing: the service owner knows what each consumer expects.
- Semantic versioning at the API level: v1 endpoints remain stable while v2 introduces breaking changes.
graph LR
subgraph Monolith
A[User Module] --- B[Order Module]
B --- C[Payment Module]
C --- D[Notification Module]
A --- D
end
subgraph Microservices
GW[API Gateway] --> US[User Service]
GW --> OS[Order Service]
GW --> PS[Payment Service]
GW --> NS[Notification Service]
US --- UDB[(Users DB)]
OS --- ODB[(Orders DB)]
PS --- PDB[(Payments DB)]
end
graph TD
Domain[E-Commerce Domain] --> OrderSvc[Order Service\nOwns: orders, line items]
Domain --> InventorySvc[Inventory Service\nOwns: stock, reservations]
Domain --> PaymentSvc[Payment Service\nOwns: charges, refunds]
OrderSvc --- ODB[(Orders DB)]
InventorySvc --- IDB[(Inventory DB)]
PaymentSvc --- PDB[(Payments DB)]
OrderSvc -->|API call| InventorySvc
OrderSvc -->|event| PaymentSvc
Interview Application
When to decompose: A problem is a good candidate for microservices when it involves multiple distinct domains that will evolve at different rates (e.g., Design Twitter: Feed, Social Graph, Media, Search are different enough in scale and team ownership to warrant separation). Decompose early when the interviewer signals high scale or organizational complexity.
When to hold off: A starter-tier problem like URL Shortener or Pastebin is fine as a modular monolith, there is no justification for the operational overhead of separate services for two or three small domains. Interviewers notice when you over-engineer.
Identifying service boundaries in a prompt: Listen for nouns in the problem description, they often map to services. "Users create posts, which appear in followers' feeds" suggests User Service, Post Service, and Feed Service as natural boundaries.
Scoring signal: Demonstrating bounded context thinking, explaining which service owns which data and why, separates strong candidates from those who just draw boxes.
Common Mistakes
Over-decomposing at the start: Jumping straight to 10 microservices before establishing the core data model and access patterns creates analysis paralysis. Start with 3-4 services and refine.
Ignoring data ownership implications: Saying "we'll split the monolith into services" while leaving a shared database behind defeats the purpose. Every service must own its own data store.
Treating microservices as a default: "It's a large system so microservices" is not a justification. The complexity cost of distributed systems is real, network partitions, distributed tracing, eventual consistency. Justify the choice.
Underestimating operational overhead: Each service needs its own CI/CD, monitoring, alerting, and on-call rotation. Mentioning this demonstrates production awareness that interviewers appreciate.
Key Vocabulary
| Term | Definition |
|---|---|
| microservice | An independently deployable service that owns a single bounded domain of business logic and its own data store. |
| monolith | A single deployable unit containing all application modules, sharing a codebase and database. |
| service boundary | The API contract, events, and data schema that define what a service exposes to the outside world. |
| bounded context | A DDD concept: a conceptual boundary within which a domain model has a consistent, precise meaning. |
| API gateway | A single entry point that routes requests to downstream services, handling authentication, rate limiting, and protocol translation. |
| service mesh | An infrastructure layer (e.g., Istio, Linkerd) that handles service-to-service communication policies: mTLS, retries, circuit breaking, observability. |
| service discovery | The mechanism by which services locate each other's network addresses dynamically, using a registry or DNS. |
| sidecar proxy | A co-deployed proxy (e.g., Envoy) that intercepts all inbound/outbound traffic for a service, used in service mesh architectures. |
| domain-driven design (DDD) | A software design approach that models systems around the business domain, using concepts like bounded context, aggregate, and ubiquitous language. |
| latency | The time elapsed between sending a request and receiving the first byte of the response. In microservices, each service hop adds latency. |
| throughput | The number of requests a system processes per unit of time. Microservices allow independent throughput scaling per service. |
Summary
Microservices decompose systems along business domain boundaries, with each service owning its data and deploying independently. The key interview skills are: knowing when to decompose (distinct domains, different scale profiles, independent team ownership) vs. when a monolith is appropriate (small scope, single team, starter-tier problems); defining service boundaries that minimize coupling; and explaining the operational cost honestly. The bounded context pattern is your framework for boundary decisions; the API gateway is the standard single-entry-point pattern. Show that you understand both the value and the cost.
