Decomposition Patterns — Deep Dive

Level: Intermediate
Pre-reading: 03 · Microservices Patterns · 02 · Domain-Driven Design


The Decomposition Challenge

The hardest problem in microservices isn't technology — it's knowing where to draw the lines. Wrong boundaries lead to distributed monoliths: all the complexity of distributed systems with none of the benefits.


Decompose by Business Capability

Align services with business capabilities — stable, high-level functions the business performs.

graph TD
    subgraph Business Capabilities
        A[Order Management]
        B[Inventory Management]
        C[Customer Management]
        D[Payment Processing]
        E[Shipping]
    end
    A --> AS[Order Service]
    B --> BS[Inventory Service]
    C --> CS[Customer Service]
    D --> DS[Payment Service]
    E --> ES[Shipping Service]
Business Capability Service Responsibilities
Order Management Order Service Create, update, cancel orders
Inventory Inventory Service Stock levels, reservations
Customer Customer Service Registration, profiles, preferences
Payment Payment Service Charge, refund, payment methods
Shipping Shipping Service Shipment creation, tracking

Identifying Business Capabilities

Source Method
Organization chart Business units often map to capabilities
Business processes Steps in customer journey
Value streams What delivers value to customers
Domain experts Interview to understand their world

Characteristics

Aspect Detail
Stability Capabilities change slowly
Alignment Maps naturally to teams (Conway's Law)
Cohesion Related functionality together
Autonomy Each capability can evolve independently

Decompose by Subdomain

Use DDD's subdomain concept for finer-grained boundaries. More precise than business capabilities.

graph TD
    subgraph Problem Space
        A[Core: Pricing Algorithm]
        B[Core: Recommendation Engine]
        C[Supporting: Order Processing]
        D[Supporting: Inventory]
        E[Generic: Authentication]
        F[Generic: Notifications]
    end
    A --> AS[Pricing Service]
    B --> BS[Recommendation Service]
    C --> CS[Order Service]
    D --> DS[Inventory Service]
    E --> ES[Auth0 - SaaS]
    F --> FS[SendGrid - SaaS]

Subdomain-Based Strategy

Subdomain Type Service Strategy
Core Custom microservice; best engineers; highest investment
Supporting Standard microservice; build or buy
Generic SaaS or open source; don't build

When to Use Subdomain vs Business Capability

Factor Business Capability Subdomain
Precision Coarser Finer
DDD experience Not required Helpful
Stability High High
Domain complexity Simple to moderate Complex

Strangler Fig Pattern

Incrementally migrate from a monolith to microservices by gradually replacing functionality behind a facade.

graph TD
    subgraph Phase 1
        C1[Client] --> F1[Facade]
        F1 -->|100%| M1[Monolith]
    end
graph TD
    subgraph Phase 2
        C2[Client] --> F2[Facade]
        F2 -->|Old routes| M2[Monolith]
        F2 -->|New routes| S2[New Service]
    end
graph TD
    subgraph Phase 3 - Final
        C3[Client] --> F3[API Gateway]
        F3 --> S3A[Service A]
        F3 --> S3B[Service B]
        F3 --> S3C[Service C]
    end

Implementation Steps

Step Action
1 Put a facade (API Gateway) in front of the monolith
2 Identify a bounded context to extract
3 Build the new service; deploy alongside monolith
4 Route traffic for that context to new service
5 Remove old code from monolith
6 Repeat until monolith is gone

Strangler Fig Routing

# API Gateway configuration (Kong/Envoy)
routes:
  - path: /api/orders/*
    destination: order-service  # New service
  - path: /api/inventory/*
    destination: inventory-service  # New service
  - path: /*
    destination: monolith  # Everything else

Benefits

Benefit Description
Low risk Incremental; can stop or reverse
Continuous delivery Both old and new run in production
No big bang Avoids risky "complete rewrite"
Gradual learning Team learns microservices iteratively

Risks and Mitigations

Risk Mitigation
Two systems to maintain Timebox extraction; prioritize
Data synchronization Use events; avoid dual writes
Feature creep in monolith Freeze monolith features; only bugs
Never finishing Set deadline for monolith retirement

Branch by Abstraction

Refactor within the monolith by introducing an abstraction layer, then swap the implementation.

graph TD
    subgraph Step 1 - Before
        A1[Client Code]
        B1[Old Implementation]
        A1 --> B1
    end
graph TD
    subgraph Step 2 - Introduce Abstraction
        A2[Client Code]
        I2[Interface/Abstraction]
        B2[Old Implementation]
        A2 --> I2
        I2 --> B2
    end
graph TD
    subgraph Step 3 - Parallel Implementations
        A3[Client Code]
        I3[Interface]
        B3[Old Implementation]
        C3[New Implementation]
        A3 --> I3
        I3 --> B3
        I3 -.->|Feature flag| C3
    end
graph TD
    subgraph Step 4 - Switch
        A4[Client Code]
        I4[Interface]
        C4[New Implementation]
        A4 --> I4
        I4 --> C4
    end

Implementation Steps

Step Action
1 Identify the code to replace
2 Create an abstraction (interface) over it
3 Point all callers to the abstraction
4 Build new implementation behind the abstraction
5 Use feature flag to switch implementations
6 Remove old implementation when confident

Use Cases

Scenario Example
Replace database Abstract data access; swap PostgreSQL for DynamoDB
Replace service call Abstract integration; swap REST for gRPC
Extract microservice Abstract module; swap local call for remote

Decomposition Anti-Patterns

1. Technical Decomposition

Wrong: Services based on technical layers.

graph TD
    A[UI Service]
    B[Business Logic Service]
    C[Data Access Service]
    A --> B --> C

Problem: Every feature requires changes to all services. Tight coupling.

Fix: Decompose by business capability. Each service owns its full stack.

2. Entity Services

Wrong: One service per database entity.

graph TD
    A[User Service]
    B[Order Service]
    C[OrderLine Service]
    D[Product Service]
    B --> C

Problem: OrderLine isn't a business capability; it's an implementation detail. Chatty services.

Fix: Aggregate entities into bounded contexts.

3. Nano-Services

Wrong: Services that are too small.

Sign Problem
Service does one CRUD operation Not a bounded context
Every change needs multiple services Wrong boundaries
More network calls than logic Over-decomposed

Fix: Merge into meaningful bounded contexts.


Service Sizing Guidelines

Factor Small Service Large Service
Team ownership Partial person Multiple teams
Deploy frequency Tied to others Independent
Data access Calls other services often Owns its data
Bounded context Partial One or more

The Two-Pizza Rule

A service should be owned by a team that can be fed with two pizzas (~6-10 people). If one team owns too many services, merge some. If multiple teams need to change one service, split it.


Decision Framework

graph TD
    A[New Feature] --> B{Clear bounded context?}
    B -->|Yes| C{Team available?}
    B -->|No| D[Keep in existing service]
    C -->|Yes| E[New microservice]
    C -->|No| F[Module in existing service]
    F --> G{Growing complex?}
    G -->|Yes| H[Extract later]
    G -->|No| D

How do you know if your services are too small?

Signs of nano-services: (1) Every user story requires changing 3+ services. (2) Services have chatty communication (many synchronous calls). (3) A service does only CRUD with no business logic. (4) Team context-switches constantly between services. (5) Transactions frequently span services. If these apply, merge services into coarser bounded contexts.

When should you use Strangler Fig vs. big bang rewrite?

Use Strangler Fig (almost always): when the system is in production, when risk tolerance is low, when you can route traffic incrementally. Use Big Bang (rarely): for small systems, when old system is not in production, when incremental migration is technically impossible. Strangler Fig is safer and lets you learn iteratively.

What's the difference between decomposing by business capability vs by subdomain?

Business capability is a coarser organizational concept — what the business does (Order Management, Customer Management). Subdomain is a DDD concept that's often finer-grained and focused on domain model boundaries. For complex domains, subdomain decomposition gives more precise boundaries. For simpler domains or organizations new to DDD, business capability is sufficient.