How to split a system into services along business-capability boundaries (not technical layers), using bounded contexts, and the concrete red flags that mean a proposed split is wrong.
Published September 23, 2026
Once requirements are gathered (Requirement Gathering Practice) and before any boxes get drawn, an HLD interview needs one more decision made explicitly: how many services, and split along what lines? This is the step most candidates skip straight past, jumping to a plausible-looking box diagram without ever stating the reasoning for where the boundaries are.
WRONG (technical layers as "services"):
"Database Service" | "Business Logic Service" | "API Service"
β these aren't independently deployable or independently scalable; they're just
a single application's internal layers relabeled as "microservices"
RIGHT (business capability):
Order Service | Inventory Service | Payment Service | Notification Service
β each owns a complete vertical slice: its own data, its own business rules,
its own API, deployable and scalable independently of the others
The most common decomposition mistake in an interview is splitting by technical layer (a "data access service," a "validation service") rather than by business capability. Technical-layer splits don't actually decouple anything β every request still has to traverse all the layers in lockstep, so you've added network hops and deployment complexity without gaining any of microservices' real benefits (independent scaling, independent deployment, fault isolation).
Bounded context (from Domain-Driven Design) is the sizing principle: a boundary within which a specific business concept has one consistent meaning and model. "Customer" means something different to the Billing service (payment methods, invoices) than to the Support service (ticket history, contact preferences) β each service should own its own model of "Customer" scoped to what it actually needs, rather than one shared "Customer" entity every service depends on. Forcing a single shared model across bounded contexts is a common root cause of tightly-coupled services that all break together.
A strong interview answer names the services and the reasoning: "I'm splitting Order and Inventory into separate services because they have different scaling profiles β Inventory reads dominate at 100x the write rate, Order is write-heavy and needs strong consistency for the order state machine β and different consistency needs, which argues for them being independently scalable and independently deployable." Naming why a boundary exists (not just drawing it) is what distinguishes decomposition from decoration.
Q: How many services is 'too many' for a given system? A: There's no fixed number β the right question isn't service count but whether each service is independently valuable to split out (different scaling needs, different team ownership, different deployment cadence); splitting purely for the sake of having more, smaller services without a concrete driver adds coordination overhead for no real benefit.
Q: What if a business capability is genuinely too small to justify its own service? A: It's reasonable to keep it as a module within a larger service initially and extract it later once it has a clear independent scaling or ownership need β premature decomposition into a service that's called by only one other service and has no independent scaling story is itself a design smell, not a virtue.
Q: How does domain decomposition interact with the database-per-service pattern? A: They're the same decision viewed from two angles β a correct bounded-context split naturally implies each service owns its own data store (see Data Ownership Model); if the proposed services would need to share a database to function, that's a strong signal the decomposition boundary itself is wrong, not just an implementation detail to work around.
Q: Can decomposition change over a system's lifetime? A: Yes, and this is expected β a system's early bounded contexts are often merged or re-split as real usage patterns and team structure become clear; the framework's job is to produce a defensible starting boundary given current requirements, not a permanent, unchangeable structure.