Featured image of post From Monolith to Microservices: Architecture Is the Destination, Migration Is the Plan

From Monolith to Microservices: Architecture Is the Destination, Migration Is the Plan

A monolith can become difficult to change long before it becomes impossible to run.

Deployments start affecting unrelated areas. A change in refunds requires regression testing checkout. One database becomes the integration layer between half the system. Teams wait for each other. Releases grow larger, rollback becomes scarier, and nobody is completely sure where one business capability ends and another begins.

At that point, somebody usually draws this:

From monolithic to microservices 1

The destination looks simple.

The migration is not.

Moving from a monolithic architecture to microservices is not primarily a matter of creating smaller applications. It is a controlled transfer of responsibility across boundaries while the existing system continues serving production traffic.

That makes it both an architectural problem and a project-management problem.

A useful migration therefore needs more than a target architecture.

It needs a path.

Start with the reason, not microservices

Before decomposing anything, ask a more uncomfortable question:

What problem are we actually trying to solve?

Microservices introduce their own costs: network failures, distributed tracing, eventual consistency, deployment coordination, infrastructure overhead, data ownership problems, versioned contracts, retries, duplicate messages, and substantially more operational surface area.

If the real problem is simply poor module boundaries inside the codebase, a modular monolith may be the better answer.

A migration should therefore start with measurable architectural drivers:

  • independent deployment of high-change capabilities;
  • different scaling characteristics;
  • clearer team ownership;
  • fault isolation;
  • regulatory or security boundaries;
  • independent technology evolution;
  • reducing coupling around specific business capabilities.

This is where the previous discussion about Spikes and ADRs becomes useful.

A Spike can investigate whether extracting a capability actually improves the characteristics we care about.

An ADR can record why that boundary was selected and which consequences were knowingly accepted.

From monolithic to microservices 2

The ADR tells us why.

The migration plan tells us how and when.

Those are different artifacts.

Do not migrate the monolith. Migrate a capability.

Imagine a payment platform containing:

From monolithic to microservices 3

A dangerous project starts with:

We are migrating the monolith to microservices.

That scope is enormous and gives the programme very few meaningful intermediate success states.

A better question is:

Which capability should we extract first?

Suppose Refunds is selected because it has clear business semantics, changes frequently, integrates with unreliable external providers, and has different scaling and failure characteristics from checkout.

Now the project becomes much more concrete:

From monolithic to microservices 4

That is something architecture and delivery teams can actually manage.

The target architecture is not the migration architecture

This distinction is easy to miss.

The desired end state might be:

From monolithic to microservices 5

But production cannot teleport from the current state to that diagram.

For some period of time, the real architecture will look more like this:

From monolithic to microservices 6

That intermediate state may live for weeks or months.

It is not temporary clutter to ignore.

It is the production architecture during the migration.

And it needs to be designed.

The Strangler Fig pattern

One of the best-known approaches to incremental replacement is Martin Fowler’s Strangler Fig Application pattern.

Instead of rewriting the entire system and switching everything at once, new functionality gradually grows around the old system.

Conceptually:

From monolithic to microservices 7

But the percentages are less important than the principle:

Replace capabilities incrementally while keeping the system operational.

This creates something extremely valuable in architecture programmes:

reversibility.

If the first extraction performs badly, creates unexpected coupling, or proves operationally expensive, the organisation has learned something without betting the entire platform.

That is architecture behaving like risk management.

Migration needs phases

Large architectural initiatives become much easier to reason about when separated into explicit phases.

A practical structure might look like this:

From monolithic to microservices 8

Phase 1 — Discovery

Understand why the capability is being extracted.

Map dependencies.

Identify database tables, APIs, scheduled jobs, events, operational dashboards, external integrations, and other modules that depend on it.

Run Spikes where uncertainty is material.

Produce ADRs for significant decisions.

The output is not yet a microservice.

It is reduced uncertainty.

Phase 2 — Establish the boundary

Define what the new service owns.

For Refunds, that might mean:

Refund Service owns:

- refund lifecycle
- refund state machine
- idempotency
- provider interaction
- retry policy
- refund persistence
- refund events

The boundary matters more than the framework.

A Spring Boot service with the wrong business boundary is still the wrong architecture.

Phase 3 — Build alongside the monolith

Do not immediately remove the existing path.

Create the new service while production continues through the monolith.

From monolithic to microservices 9

This gives the team somewhere to migrate traffic gradually.

Phase 4 — Validate in parallel

Before trusting the new path with all production traffic, compare behaviour.

Depending on the capability, techniques can include:

From monolithic to microservices 10

The second path does not necessarily execute external side effects. For payments and refunds, blindly duplicating a production command would obviously be dangerous.

But calculations, transformations, routing decisions, or state transitions can often be compared safely.

This turns migration from:

We think the new service works.

into:

Across 1.8 million representative operations, the old and new implementations agreed in 99.99% of the cases, and we understand the remaining differences.

That is a much stronger basis for moving traffic.

Move traffic, not hope

Eventually the new service must become authoritative.

Do not think only in terms of deployment.

Think in terms of traffic migration.

From monolithic to microservices 11

A migration might progress through controlled stages:

Stage 1     100% old     0% new
Stage 2      95% old     5% new
Stage 3      75% old    25% new
Stage 4      25% old    75% new
Stage 5       0% old   100% new

The exact percentages are implementation details.

The important part is that each stage has entry criteria, observations, and rollback conditions.

For example:

Proceed if:

error rate       < threshold
p95 latency      < threshold
duplicate rate   = expected
reconciliation   = clean
DLQ growth       = normal
provider errors  = understood

Architecture has now become measurable.

Data is usually the hard part

Extracting Java or TypeScript code into another repository is rarely the hardest part.

Data ownership is.

The monolith may currently do this:

From monolithic to microservices 12

Creating four services that all continue directly accessing those same tables gives us:

From monolithic to microservices 13

We have distributed the applications while preserving much of the original coupling.

This is sometimes a valid transitional state.

It is a poor place to stop accidentally.

A stronger destination is explicit ownership:

From monolithic to microservices 14

Now another problem appears.

A database transaction can no longer quietly coordinate everything.

Distributed boundaries force previously hidden architectural questions into the open:

  • What is the source of truth?
  • Which service owns the state transition?
  • What happens when a downstream call fails?
  • Do we need eventual consistency?
  • What is retried?
  • What must be idempotent?
  • What happens if an event is delivered twice?
  • How do we reconcile inconsistent states?

Microservices do not eliminate complexity.

They move complexity across boundaries.

Project management has to follow the architecture

Traditional project plans often describe migration in technical tasks:

Create repository
Create service
Create database
Implement endpoints
Deploy service

All of those tasks can be completed while the migration itself remains nowhere near finished.

A more meaningful plan tracks capability ownership.

For example:

Refund Extraction

[✓] Boundary agreed
[✓] ADRs accepted
[✓] Service operational
[✓] Observability available
[✓] Data ownership established
[✓] Shadow validation passed
[✓] 5% production traffic
[✓] 25% production traffic
[ ] 100% production traffic
[ ] Old refund path disabled
[ ] Old database access removed
[ ] Dead code removed

This changes what “done” means.

The service existing is not the milestone.

The responsibility having moved is.

Every migration step needs an exit condition

One of the most useful disciplines in architecture programmes is defining what allows the team to move to the next phase.

From monolithic to microservices 15

Without exit criteria, migrations tend to move according to calendar pressure:

We planned to switch on Thursday.

With exit criteria, the decision becomes evidence-based:

Error rate, latency and reconciliation are inside the agreed thresholds, so we can increase traffic.

That is a very different engineering culture.

Observability is part of the migration architecture

A migration without observability is extremely difficult to control.

Before moving production traffic, the new boundary should expose enough information to answer:

Is it working?
Is it slower?
Is it failing differently?
Are retries increasing?
Are duplicate requests appearing?
Where is time being spent?
Can we correlate the request across systems?

That means metrics, logs, and distributed traces are not something to “add later.”

They are part of the migration mechanism.

During the transition, a trace may cross both worlds:

From monolithic to microservices 16

Without correlation across that boundary, operational complexity increases precisely when the architecture is already at its most complicated.

Decommissioning is part of the project

Migration projects have a dangerous moment:

New service works.
Production uses it.
Everyone celebrates.

But the old implementation remains.

Six months later:

Refund Service
      +
Old Refund Module
      +
Old Tables
      +
Old Jobs
      +
Compatibility Code
      +
Temporary Routing

The organisation now owns more architecture than before the migration.

Decommissioning therefore belongs in the original plan.

From monolithic to microservices 17

Deleting the old architecture is not cleanup.

It is the final migration phase.

Architecture and project management meet in the middle

A system architect can draw an excellent target:

From monolithic to microservices 18

A project manager can build an excellent roadmap:

Q1 → Q2 → Q3 → Q4

Neither is sufficient alone.

The useful artifact connects them:

From monolithic to microservices 19

This is where architecture stops being a picture of the future and becomes an executable strategy.

The migration itself is architecture

There are really three architectures involved:

CURRENT STATE
TRANSITION ARCHITECTURE
TARGET STATE

Teams naturally spend most of their design effort on the first and third.

The middle deserves just as much attention.

During migration, the organisation may temporarily operate duplicate paths, compatibility APIs, routing rules, event bridges, shared databases, shadow traffic, feature flags, and synchronization mechanisms.

Those components may never appear in the final diagram.

They still need architecture decisions, ownership, observability, security, testing, and explicit retirement plans.

That is why a large architectural migration is not simply a refactoring exercise.

It is the controlled evolution of a running system.

A useful mental model

The entire approach can be reduced to:

From monolithic to microservices 20

Or even more compactly:

Architecture defines the destination. Migration architecture defines the safe path. Project management makes that path executable.

A monolith does not become a healthy microservice architecture because enough services were created.

The migration succeeds when responsibilities, data ownership, traffic, operational ownership, and failure handling have moved deliberately — and the old architecture can finally be deleted.

That is a much more meaningful definition of done.

References

  1. Fowler, M. — Strangler Fig Application. MartinFowler.com.
  2. Newman, S. — Monolith to Microservices: Evolutionary Patterns to Transform Your Monolith. O’Reilly Media.
  3. Newman, S. — Building Microservices, 2nd Edition. O’Reilly Media.
  4. Ford, N., Richards, M., Sadalage, P. & Dehghani, Z. — Software Architecture: The Hard Parts. O’Reilly Media.
  5. Richards, M. & Ford, N. — Fundamentals of Software Architecture: An Engineering Approach. O’Reilly Media.
  6. Nygard, M. — “Documenting Architecture Decisions.”
  7. Fowler, M. — Feature Toggles (aka Feature Flags).
  8. Corebaseit — Spike First, ADR Second: Evidence and Rationale in System Design.