Stackbook Logo
migration

Monolith Decomposition Framework

Intent

Break a monolith into independently deployable services in measured steps, guided by business boundaries and data ownership.

Problem

A large monolith slows delivery because every change risks unrelated modules and shares one release cycle. Teams want service independence but fear a rewrite. Splitting along technical layers or without a data plan creates a distributed monolith.

Forces

    Solution

    Monolith Decomposition Framework

    Decomposition is a sequence of small, reversible extractions, each justified by a measurable benefit. The framework favors seams and data ownership over technical slicing, and it treats a modular monolith as a legitimate stopping point.

    Decomposition is justified by outcomes, not architecture fashion. Define success before starting, for example reducing lead time for a given team by half, and stop extracting when the measured benefit no longer exceeds the operational cost of another service.

    How it works

    First, map the domain and dependencies: modules, shared tables, call graphs, and change history. Candidates with high change frequency and low coupling go first. A seam is introduced in the monolith, for example an interface with a single implementation. A proxy or gateway routes selected requests to the new service. Data moves by giving the service its own store, populated through change data capture or dual writes, and the monolith later reads through the service API.

    Implementation steps

    1. Build a dependency and data-access map from code and database query logs.
    2. Define bounded contexts with domain experts and rank extraction candidates.
    3. Make the module boundary explicit inside the monolith and remove cross-module table access.
    4. Stand up platform basics: CI/CD, observability, service templates, and a gateway.
    5. Build the new service and run it in shadow mode, comparing outputs to the monolith.
    6. Shift traffic gradually using percentage-based routing.
    7. Cut data ownership over, then delete the old code and tables.
    8. Review delivery metrics and decide on the next extraction.

    Keep the extraction team small and stable, and give it explicit time to delete old code. Without deletion, the migration only adds systems and the maintenance burden grows instead of shrinking.

    Common pitfalls

    • Extracting services that still share a database, preserving coupling.
    • Chatty synchronous calls between new services, causing latency and cascading failures.
    • Leaving dead code in the monolith after cutover.
    • Splitting by layer such as "the data service" rather than by capability.

    Metrics to watch

    Lead time and deployment frequency of affected teams, change failure rate, number of cross-service calls per request, lines and tables remaining in the monolith, p99 latency on migrated flows, and incident count related to integration.

    Share the numbers with leadership every quarter. A decomposition without visible improvement in delivery speed or reliability erodes sponsorship, and the program should adjust or stop rather than continue by inertia.

    Example routing rule

    routes:
      - match: /api/invoices/*
        destination:
          - service: invoicing-service
            weight: 25
          - service: legacy-monolith
            weight: 75
        shadow:
          service: invoicing-service
          compare_responses: true
        rollback: set weight of invoicing-service to 0
    

    Key takeaways

    • Choose boundaries from the domain, not from layers.
    • Move data ownership with the code, or coupling remains.
    • Shadow and shift gradually; keep rollback trivial.
    • A well-structured modular monolith can be the right end state.

    ✓ When to Use

    • •Release cadence is constrained by a shared deployment
    • •Parts of the system have very different scaling needs
    • •Team count has grown beyond effective single-codebase work
    • •Clear domain boundaries exist or can be discovered

    ✗ When Not to Use

    • •When the team is small and the monolith is not a bottleneck
    • •When the problem is code quality and a modular monolith would suffice
    • •When operational maturity for distributed systems is missing

    Pros

    • +Incremental risk with reversible steps
    • +Independent deployment and scaling
    • +Clear ownership by team
    • +Improves domain understanding

    Cons

    • −Distributed systems complexity arrives early
    • −Data splitting is hard
    • −Temporary duplication and dual-running cost
    • −Can stall midway, leaving two systems

    Cost Profile

    Infrastructure

    Operational

    Cognitive

    Failure Modes

      Alternatives

        Related Patterns

          Competency Domains

          economics evolutiondata statedistribution communication