Enterprise platform · TVT Group, 2021–2024

Strangler migration from a .NET Framework monolith

One large .NET system was replaced piece by piece — one business area at a time, while the old system kept running. Deployments fell from about two hours of manual work to about fifteen minutes.

.NET 7 · RabbitMQ · PostgreSQL · AWS EC2

Result: Deploy time from about 2 hours to about 15 minutes after CI/CD and service split; first domain service extracted without a big-bang rewrite.

The problem

A single .NET Framework 4.7 monolith meant every release risked every domain, and a full deploy took about two hours of manual steps.

Production traffic could not stop for a rewrite.
Shared database coupling made naive splits dangerous.
Several enterprise accounts depended on strict response expectations.
New projects were still spinning up on inconsistent AWS setups.

System architecture

flowchart LR
  subgraph legacy ["Legacy monolith"]
    legacy_0[".NET Framework 4.7 monolith"]
    legacy_1["Manual deploy pipeline"]
  end
  subgraph extract ["Strangler cut"]
    extract_0["First domain service on .NET 7"]
    extract_1["Traffic router or gateway"]
    extract_2[("Shared DB phase")]
  end
  subgraph platform ["Event and ops"]
    platform_0[["RabbitMQ event bus"]]
    platform_1["CI build and test"]
    platform_2["Automated deploy to EC2"]
    platform_3["AWS standard template"]
  end
  legacy_0 --> extract_1
  extract_1 --> extract_0
  extract_0 --> extract_2
  extract_0 --> platform_0
  platform_1 --> platform_2
  platform_3 --> platform_2

Key architecture decisions

Strangler split by domain, not big-bang rewrite

Run new .NET 7 services beside the monolith and shift traffic gradually.

A rewrite freeze would stall fixes for paying accounts.

Rejected Big-bang rewrite — rejected because risk and calendar were unacceptable.

Trade-off Temporary dual-run operational cost and shared-database coupling until a later cut.

Async end-to-end on I/O-bound paths

Use async through Web API and data access on hot paths; remove session affinity where possible.

Thread pool starvation under load was a recurring monolith pain.

Rejected Blocking .Result calls on legacy sync wrappers — rejected on new services after deadlocks in Framework hosting.

Trade-off Team retraining and careful audit of legacy call sites still on the monolith.

Standard AWS and CI template for new projects

One default EC2, security group, Docker Compose, and CI checklist cloned per project.

Each new engagement was re-inventing TLS and deploy steps.

Rejected Per-project custom infra — rejected after repeated week-long onboarding delays.

Trade-off Template drift must be owned; exceptions need written justification.

What I own, wrote, and delegate

Own

  • Migration strategy and which domain to cut first
  • CI/CD standardization and AWS onboarding template for new projects
  • Technical point of contact for 5 enterprise accounts during migration windows

Wrote

  • First extracted .NET 7 services and integration with the remaining monolith
  • Event-driven paths where domains needed async integration

Delegate

  • Per-account commercial negotiation and account management

Scale

  • Monolith deploy about 2 hours before automation; about 15 minutes after CI/CD and smaller services
  • New project AWS onboarding reduced from weeks to days with a shared template
  • 5 enterprise accounts with me as technical point of contact for incidents

Boundaries

  • Load improvement came in layers (async I/O, state out of process, cache, indexes, runtime upgrade), not a single magic number.
  • Cutover was not a zero-downtime rolling deploy for every service.

What we watch in operation

Deploy duration from merge to production traffic on new serviceError rate during traffic shift windowsQueue depth when monolith and new service publish together

Rules the design never breaks

  • No single-domain deploy may require a full-monolith outage.
  • Shared schema changes are coordinated; services do not silently fork data meaning.
  • Enterprise accounts get a named technical contact during migration, not only ticket queues.

Broader application

  • Legacy .NET Framework estates that must move without stopping revenue.
  • Organisations standardising cloud onboarding across many projects.
  • Teams learning microservices after years of monolith releases.