← Back to blog

Microservice Decomposition: An Engineering Decision Framework

Breaking a monolith into microservices is not about "making it smaller" — it is about splitting it right, and splitting it only when the cost is worth it. This article gives a decision framework covering when to decompose, how to cut boundaries, how fine is fine enough, and the governance traps that follow — for architects and tech leads evaluating or already on the microservice path.

The Bottom Line: Microservices Are Not a Goal — They Are a Trade-Off

Many teams treat “going microservices” as the terminal of technical evolution. It is not. Microservices are a price you pay to solve specific constraints — independent deploy, independent scaling, independent teams. The price includes: network latency, distributed transactions, service governance, harder debugging, and increased deployment complexity.

So the first question is not “how to split” — it is “do I need to split at all?”

This decision framework answers three questions in order:

  1. To split or not — when the monolith actually hurts
  2. How to split — from monolith to microservices step by step
  3. After the split — common governance traps

1. To Split or Not: When the Monolith Actually Hurts

The Monolith’s Strengths (Do Not Underestimate)

The monolith is still optimal for:

  • Teams of 1-5 people
  • Unclear business domain boundaries (exploration phase)
  • Pre-PMF product stage
  • High data consistency requirements (e.g., core financial ledger)

Under these conditions, a monolith outperforms microservices in development speed, ops simplicity, and debugging convenience.

The Pain Threshold

When should you start thinking about splitting? Not when the codebase hits a certain line count — but when team coordination cost exceeds the simplicity benefit of the monolith. The signals:

IndicatorPain SignalHow to Measure
Deploy frequencyA feature is blocked by other modules’ issuesWeekly deploys < 3
Code conflictsEvery merge touches the same filesMonthly conflicts > 5
Blast radiusOne module’s bug takes down the whole serviceP0 incidents per quarter
Scaling efficiencyOnly one module needs scaling, but you redeploy everythingOne module > 80% utilization, others < 20%
Tech lock-inSwitching one module’s tech stack is an impossible refactorEstimated refactor time > 2 weeks

Any 3 of these signals → it is time to seriously consider decomposition.


2. How to Split: Step by Step

Step 1: Draw Boundaries by Business Subdomain (Lightweight DDD)

Do NOT split by technical layer (“extract the Controller”, “extract the Model”). Split by business capability.

Example: an e-commerce system

User domain: registration, login, permissions, addresses
Product domain: catalog, inventory, categories, search
Order domain: checkout, payment, refund, shipping
Content domain: descriptions, reviews, images, videos

Each domain is a potential microservice. The judgment rule: changes in one domain do not require synchronous knowledge from other domains — cross-domain information flows through events.

Step 2: Identify Split Candidates

Not all domains have equal split priority. Rank by two dimensions:

  • Change frequency: how often this domain’s logic changes
  • Resource consumption: CPU/memory/storage demand
High change + High resource → split first (search, recommendation)
High change + Low resource → split second (user, permissions)
Low change + High resource → split on demand (logs, reports)
Low change + Low resource → keep in monolith last (config, lookups)

Step 3: Start with the Easiest Module to Extract

Do not attempt a one-shot N-service decomposition. Use the Strangler Fig Pattern:

  1. Identify one module that can run independently
  2. Build a separate API layer for it (new traffic goes to new API, old traffic continues via monolith)
  3. Gradually migrate callers to the new API
  4. Once migration is complete, remove the module from the monolith
  5. Repeat

One service at a time, validate each: independent deploy, independent test, independent scaling.


3. After the Split: Four Common Governance Traps

Trap 1: Distributed Transactions

The most insidious microservice pitfall. What was a single database transaction in the monolith is now distributed.

Mitigation:

  • Prefer eventual consistency over strong consistency
  • When unavoidable: use the Saga pattern (choreography or orchestration)
  • Avoid cross-service transactions entirely — redesign boundaries so the transaction falls within one service

Trap 2: Deep Call Chains

A → B → C → D → E

Each hop multiplies latency. Failure probability rises exponentially with chain depth.

Mitigation:

  • Keep chain depth ≤ 3
  • Beyond 3, use async events or data duplication
  • Every synchronous call must have a timeout and circuit breaker

Trap 3: Shared Database

“Split the services, deal with the database later” — the most common half-baked state. Services are decomposed, but they all read from the same database.

Mitigation:

  • Each service owns its database (or its own schema)
  • Cross-service data exchange goes through APIs, never through the database
  • For cross-service queries, use caching or CQRS

Trap 4: Over-Decomposition

A 10-person team maintaining 20 microservices — each person handling 2 services, and each feature change touches 3-5 services.

Diagnosis: if one feature change requires modifying more than 3 services, you have over-decomposed. Merge services until the “feature-change radius ≤ 2 services.”


PhaseGoalDurationOutput
Phase 0Modularize inside the monolith1-2 monthsClear module boundaries, interface definitions
Phase 1Extract 1-2 high-value services2-3 monthsIndependent deploy and scaling
Phase 2Extract 3-5 core services3-6 monthsFull service governance
Phase 3Evolve on demandOngoingBusiness-driven, never split for its own sake

Each phase targets “the most painful constraint right now” — not a service count target.


Summary

QuestionAnswer
When to split3+ pain signals from the threshold table
What to split byBusiness subdomain (lightweight DDD), not technical layer
Where to startHigh-change + high-resource modules
How to splitStrangler Fig, one at a time, validate each
How fine is enoughFeature-change radius ≤ 2 services
Biggest trapServices split but the database stayed shared

Microservice maturity is not measured by how many services you have — it is measured by whether you can independently deploy, test, and scale any single service without materially impacting the business.

Need an architecture review, decomposition plan, or migration roadmap? Contact us — tell us about your system and pain points, feasibility within 24 hours.

FAQ

How fine should a microservice be?

There is no universal answer, but a rule of thumb: a service is fine enough if one person can independently understand, develop, and deploy it. If splitting further means a single feature change touches N services, you have gone too far. The right boundary is "business capability" — each service corresponds to a complete business subdomain, not a database table or a single CRUD operation.

Should each service have its own database?

Start with logical isolation, not physical separation. Use schemas or database namespaces so that services access each other only via APIs, never by reading tables directly. When a service data volume or query pressure warrants independent scaling, promote it to a separate database instance. Logical isolation costs far less to migrate than splitting a shared database across multiple instances.

Synchronous or asynchronous for inter-service communication?

Principle: queries go synchronous (REST/gRPC), commands go asynchronous (message queue). Sync calls are fine for real-time lookups but introduce call-chain dependencies and cascade failures. Async calls decouple timing dependencies. In practice, most service-to-service communication should be async; only queries where the caller must block for the result should be sync.

When should you NOT decompose into microservices?

Three cases: ① Team < 5 people — ops overhead will crush a small team; a modular monolith is better. ② Unclear business domain boundaries — if you cannot delineate business subdomains clearly, your service boundaries will keep shifting. ③ Pre-PMF stage — rapid iteration is the priority, and microservice deploy/debug overhead will slow you down.

This article comes from AI Enable Harness front-line delivery practice. Need a similar system or optimization service?

Subscribe to Updates

Get notified when new articles are published. No spam, occasional updates only.

Subscribe →