smaple.tr
microservices migration strategy

Microservices Migration Strategy: Monolith Decomposition, Domain Boundaries, and Strangler Pattern [2026]

Mehmet Kurtipek
November 23, 2025
11 min read
microservices migration strategy
monolith decomposition
domain-driven design
strangler fig
Conway's Law
distributed systems

Microservices migration is one of the most commonly misapplied patterns in software engineering. Teams adopt it because Netflix, Amazon, and Uber use it — not because their organizational or technical constraints resemble Netflix, Amazon, or Uber. The result is distributed monoliths: systems with microservices deployment complexity and none of the independence benefits, because service boundaries were drawn along technical layers rather than business domains.

This guide covers microservices migration strategy as an organizational and technical discipline: when migration is justified, how to identify correct service boundaries using domain-driven design, the strangler fig migration pattern, distributed data management, team structure requirements, and the most common failure modes. By the end, you will have a clear decision framework for whether and how to migrate.

Microservices Migration Strategy: When It Is Justified

The microservices migration decision is an organizational decision, not a technical one. Amazon's API gateway team famously summarized this: microservices solve an organizational problem (team coordination) more than a technical problem (scaling). If your organization does not have the coordination problem, you do not have the problem microservices solve.

The Monolith's Constraints That Require Migration

Team coordination bottleneck: multiple teams cannot ship features independently because their code is in the same repository, deploys together, and breaks each other's work. Each deployment requires cross-team coordination. Hotfixes require full regression testing. This is the primary driver for microservices adoption — not performance or scalability.

Independent scaling requirements: your search API handles 10,000 requests/second. Your admin API handles 50 requests/second. In a monolith, you scale both together. If the cost of over-provisioning the admin API is significant at your scale, independent scaling creates real cost savings.

Technology heterogeneity: one component requires Python (data processing), another requires Java (high-performance API), another requires Go (network-intensive service). A monolith forces a single technology stack.

Failure isolation: a memory leak in one component crashes the entire application. Independent services limit blast radius to the failing service (with appropriate circuit breakers).

When Migration Is Not Justified

System is too small: fewer than 5-10 engineers, single team, early product. The operational overhead of service discovery, distributed tracing, and inter-service communication exceeds the coordination savings.

The scaling problem is the database: a monolith with read replicas and a caching layer handles most scaling requirements. Database sharding, read replicas, and Redis handle most cases that do not justify microservices operational complexity.

The team coordination problem is organizational, not architectural: if the team cannot collaborate effectively on a monolith, a distributed system will not fix it. Conway's Law says your system will reflect your communication structure. Fix the team structure before fixing the architecture.

The team lacks distributed systems experience: microservices require operational knowledge that monolith engineers do not have — service discovery, distributed tracing, circuit breakers, distributed transactions, network failure handling. The learning curve is months, not weeks.

Domain-Driven Design: Finding Service Boundaries

The most critical decision in microservices migration is where to draw service boundaries. Wrong boundaries create the worst outcome: a distributed monolith where services must call each other synchronously for every operation, network failures cascade into business failures, and the deployment complexity of microservices provides none of their independence benefits.

Bounded Contexts

Domain-Driven Design (DDD) provides the framework for identifying correct service boundaries. A bounded context is a cohesive area of business functionality with its own model, language, and behavior that makes sense as a unit.

For an e-commerce platform, bounded contexts include:

  • Product Catalog: product information, categories, attributes, pricing
  • Inventory: stock levels, reservations, warehouse location
  • Shopping Cart: active sessions, cart items, pricing at time of add
  • Order Management: order creation, state machine, order history
  • Payment: payment methods, charge/refund, dispute management
  • Fulfillment: shipping, tracking, carrier integration
  • Customer Account: profile, preferences, order history view
  • Search: indexing, query, faceting, personalization

Each bounded context has a clear business owner, a distinct data model, and a defined interface. Crucially: Product Catalog and Inventory are separate bounded contexts even though they relate to the same physical products. They have different models (catalog cares about attributes and descriptions; inventory cares about quantities and locations) and different rate of change.

Context Mapping

Before migrating, map the relationships between bounded contexts:

Upstream/downstream: Inventory consumes events from Order Management to decrement stock. Order Management is upstream; Inventory is downstream.

Shared kernel: Catalog and Search share a product representation schema. Both teams must coordinate on changes to this schema.

Anti-corruption layer: when integrating with an external system (payment processor, shipping carrier) that uses different models, a translation layer prevents the external model from corrupting your domain model.

Conformist: when a downstream service simply accepts whatever the upstream provides without adaptation. Avoid this pattern — it creates tight coupling between teams.

Document context relationships before migrating. Services with complex bidirectional relationships are poor candidates for early migration — extract well-bounded, loosely-coupled contexts first.

Event Storming

Event Storming is a collaborative workshop technique for discovering domain events and bounded contexts. Participants include domain experts (product managers, business analysts) and engineers. The workshop produces:

  1. Domain events (past tense: "OrderPlaced", "PaymentProcessed", "ItemShipped")
  2. Commands that trigger events ("PlaceOrder", "ProcessPayment", "ShipItem")
  3. Aggregates that handle commands and produce events
  4. Policy rules that react to events and trigger commands
  5. External systems that produce or consume events

The bounded contexts emerge from the clusters of closely related events, commands, and aggregates. Events that always occur together, aggregates that share the same invariants, and commands that require the same authorization — these form natural service boundaries.

Strangler Fig Migration Pattern

The strangler fig pattern is the standard approach for zero-downtime monolith decomposition. Named after the strangler fig tree, which grows around a host tree until the host is replaced.

Migration Sequence

Step 1: Traffic routing layer. Install an API gateway or reverse proxy in front of the monolith. Initially, all traffic passes through to the monolith unchanged. This establishes the routing infrastructure required for incremental migration.

Step 2: Select the first service candidate. Choose a bounded context that is:

  • Well-defined and relatively self-contained
  • High value if migrated (demonstrates ROI)
  • Manageable data migration scope
  • Not depended on by every other part of the system

Authentication, product search, and notification delivery are common first candidates.

Step 3: Build the standalone service. Build the replacement service with full test coverage and observability. Do not attempt to improve functionality at the same time as extraction — parity first, improvements after.

Step 4: Dark launch and validation. Route a small percentage of traffic (1-5%) to the new service while the monolith continues serving most requests. Compare responses between the two implementations. Catch behavioral differences before they affect users.

Step 5: Incremental traffic shift. Increase traffic to the new service: 5% → 25% → 50% → 100%. Monitor at each step: latency, error rates, business metrics (conversion rate, order completion rate). A degradation at 25% that was not visible at 5% indicates a scalability or correctness issue.

Step 6: Data cutover. Migrate data from the monolith's database to the service's own data store. Use dual-write (write to both) during transition, validate consistency, then promote the service's data store to primary.

Step 7: Decommission monolith component. Remove the corresponding code from the monolith. Delete the routing rule that pointed to the monolith for this domain.

Step 8: Repeat. Select the next candidate and repeat.

Migration Velocity

Realistic timeline: one bounded context per 2-4 months, depending on complexity. A monolith with 10 well-defined contexts: 18-36 months for full decomposition. Do not schedule an aggressive migration — the first few migrations are learning experiences that calibrate the cost of subsequent migrations.

Accept that the monolith will run alongside new services for 2-3 years. Design the migration to be durable, not fast.

Distributed Data Management

Data management is the hardest part of microservices migration. Each service should own its data — no shared databases between services. This creates challenges that do not exist in monolithic architectures.

Database Per Service

Every service has its own data store, inaccessible to other services except through the service's API. This is the correct design but requires answering questions that a shared database handles automatically.

Data consistency: in a monolith, BEGIN TRANSACTION; UPDATE orders; UPDATE inventory; COMMIT; is atomic. In microservices, there is no distributed transaction that spans services. Instead:

Saga pattern: a sequence of local transactions, each publishing an event that triggers the next step. If any step fails, compensating transactions undo previous steps.

Example: Order placement saga:

  1. Reserve inventory (Inventory service)
  2. Process payment (Payment service) — if fails, release reservation
  3. Create order record (Order service) — if fails, refund payment and release reservation
  4. Send confirmation (Notification service) — fire and forget, no compensation needed

Sagas are choreographed (services react to events, no central coordinator) or orchestrated (a saga coordinator sends commands and handles failures). Orchestration is easier to observe and debug; choreography is simpler but harder to trace.

Eventual consistency: accept that data across services will be temporarily inconsistent. The order service may show "payment processing" for 200ms after the payment service has already completed. Design the user experience to communicate in-progress states rather than assuming all systems are synchronized.

Avoid distributed transactions: two-phase commit (2PC) across services creates tight temporal coupling, degrades performance, and fails in complex ways when network partitions occur. Sagas with compensating transactions are the correct alternative.

Read Across Services

When a view requires data from multiple services (display an order with customer name from the customer service and product names from the product service), there are two approaches:

API composition: the requesting service (or a BFF) calls each service and assembles the response. Simple but creates synchronous dependencies and high latency for complex views.

CQRS with materialized views: maintain a read-optimized view store that aggregates data from multiple services. The view store is updated asynchronously as events arrive from source services. Fast reads at the cost of eventual consistency and additional infrastructure.

For performance-critical views (e-commerce product listing pages, dashboard aggregations), materialized views in a read-optimized store (Redis, Elasticsearch) are justified. For administrative or low-frequency views, API composition is adequate.

Team Structure and Conway's Law

Conway's Law: "Organizations which design systems are constrained to produce designs which are copies of the communication structures of those organizations."

In practice: if three teams own overlapping components of a monolith, you will build a distributed monolith with three services that cannot change independently because they share data models and call each other synchronously.

Inverse Conway Maneuver

To build loosely-coupled services, create loosely-coupled teams first. Each service should be owned by a single team. That team has end-to-end ownership: development, testing, deployment, and on-call responsibility. They set their own release schedule and make technology decisions within their bounded context.

This requires a specific team size and structure. The "two-pizza team" (5-8 engineers) is the effective unit for a single service. Smaller teams cannot maintain 24/7 on-call; larger teams create too much internal coordination cost.

Platform Team

As the number of services grows, common concerns emerge: service discovery, secret management, CI/CD templates, logging pipelines, monitoring dashboards. These should be owned by a Platform (or Infrastructure) team that provides shared capabilities as a product.

The Platform team's customers are the service teams. Evaluate the Platform team by the same metrics as any product: developer satisfaction, time-to-bootstrap a new service, on-call toil reduction.

Common Failure Modes

Distributed monolith: services call each other synchronously in chains. An order request calls the inventory service, which calls the product service, which calls the pricing service. Network latency compounds; one slow service creates p99 latency across the entire chain. Fix: design for event-driven communication where possible; use caching and materialized views for data needed across services.

Premature decomposition: starting with microservices before domain boundaries are understood produces arbitrary service splits that require reorganization. Amazon's approach: start with a well-structured monolith, extract services when team coordination pain is demonstrated. Do not decompose in anticipation of pain that has not materialized.

Shared database anti-pattern: microservices that share a database are not microservices — they are processes that happen to run separately. Schema changes require coordination with all services that read the database. Deploy one service and break another. Fix: strict database-per-service, enforced by network policy that prevents direct database connections from unauthorized services.

Missing operational baseline: deploying microservices without Kubernetes (or equivalent orchestration), distributed tracing, centralized logging, and health monitoring creates unmanageable operational complexity. The operational baseline must be in place before the first microservice enters production.

Ignoring domain boundaries for performance reasons: "let's just call the database directly, it's faster." This always starts as a one-time exception and becomes a permanent pattern. Performance optimization within a service's own data is acceptable; performance optimization that breaks data ownership is not.

Microservices migration is a multi-year organizational transformation. The technical patterns are learnable; the organizational discipline — team autonomy, clear ownership, event-driven communication, database-per-service — is what most migrations underestimate and most failures trace back to.

Related Articles

August 11, 2026

MLOps Guide: Taking Machine Learning Models to Production [2026]

87% of machine learning models built by data science teams never reach production. The models work — they pass cross-validation, they score well on holdout sets, they demonstrate genuine predictive value. The problem is not the modeling. The problem is everything that happens between a notebook experiment and a reliable, monitored, production system. MLOps is the discipline that closes that gap. This guide covers the full MLOps stack: maturity levels, tooling choices (MLflow, DVC, Kubeflow

Read More
August 10, 2026

LLM Fine-Tuning Guide: Custom Model Training with LoRA and QLoRA [2026]

General-purpose LLMs are impressive. They can write code, summarize documents, answer questions, and translate between languages with reasonable accuracy. But "reasonable" is not good enough when your application requires consistent output format, domain-specific terminology, a particular tone, or behavior that the base model was never trained to exhibit. That gap is where fine-tuning matters. Fine-tuning updates a model's weights on your specific data, changing how the model behaves — not

Read More
August 9, 2026

Computer Vision Applications: Object Detection, OCR, and Industrial AI [2026]

Computer vision has moved well past the research phase. The models are trained, the frameworks are mature, the hardware is accessible, and the use cases are generating measurable returns. What was a specialized capability requiring deep expertise in 2018 is now deployable infrastructure — if you know which component to reach for and where the real complexity lives. This guide covers computer vision applications across industrial, medical, logistics, and document processing domains. It expl

Read More