Strangler Fig Pattern in C#: Incremental Legacy System Migration with Examples

Strangler Fig Pattern in C#: Incremental Legacy System Migration with Examples

The Strangler Fig pattern in C# is an architectural migration pattern where parts of a legacy application are gradually replaced with new services or modules until the old system can be completely removed.

The Strangler Fig pattern is commonly used in large .NET systems that cannot be rewritten all at once due to cost, risk, or operational constraints. Instead of replacing the entire application in a single release, developers incrementally build new functionality around the existing system while routing selected requests to the new implementation. Over time, more features are migrated from the legacy application into modern ASP.NET Core services or microservices. This approach allows both systems to coexist during migration, reducing downtime and minimizing deployment risk. The name comes from the strangler fig tree, which slowly grows around another tree until the original tree disappears.

Why We Use the Strangler Fig Pattern in C#?

We use the Strangler Fig pattern in C# to modernize legacy applications safely and incrementally without requiring a risky full-system rewrite.

Common reasons include:

• Reducing migration risk
• Avoiding long development freezes
• Maintaining continuous business operations
• Incrementally adopting ASP.NET Core or microservices
• Modernizing monolithic applications gradually
• Allowing old and new systems to coexist
• Supporting phased deployments
• Simplifying testing and rollback strategies

This pattern is especially useful for enterprise systems that have been running for many years.

When Should We Use the Strangler Fig Pattern in C#?

The Strangler Fig pattern should be used when an existing system is too large, fragile, or business-critical to replace in a single step.

Typical programming and architectural problems include:

• Large monolithic ASP.NET applications
• Legacy .NET Framework systems
• Systems with tightly coupled components
• Applications with outdated dependencies
• Gradual migration to cloud-native architecture
• Replacing SOAP services with REST APIs
• Moving from monolith to microservices
• Incremental database modernization
• Continuous delivery during migration
• Migrating business logic without downtime

It is particularly valuable when:

• The system cannot tolerate long outages
• The application is actively used in production
• Full rewrites are too expensive or risky
• Teams want continuous modernization
• Different modules can be migrated independently

Example Use Cases of Strangler Fig Pattern in C#

1. Migrating Legacy ASP.NET MVC Endpoints to ASP.NET Core

An old ASP.NET MVC application gradually moves endpoints into a new ASP.NET Core API.

// Legacy Application
public class LegacyOrderController : Controller
{
    public ActionResult Details(int id)
    {
        return Content($"Legacy Order Details: {id}");
    }
}

// New ASP.NET Core Service
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/api/orders/{id}", (int id) =>
{
    return Results.Ok(new
    {
        OrderId = id,
        Source = "Modern ASP.NET Core Service"
    });
});

app.Run();

// Reverse Proxy Routing
app.MapWhen(
    context => context.Request.Path.StartsWithSegments("/api/orders"),
    modernApp =>
    {
        modernApp.RunProxy(new ProxyOptions
        {
            UpstreamBaseUri = "http://localhost:6001"
        });
    });

Benefits

• Incremental endpoint migration
• No full-system downtime
• Safe rollout strategy

2. Extracting Authentication into a Modern Identity Service

A monolithic application delegates authentication to a new Identity service.

// Legacy Application Delegating Authentication
public async Task<bool> ValidateUserAsync(
    string username,
    string password)
{
    using var client = new HttpClient();

    var response = await client.PostAsJsonAsync(
        "https://identity.howcsharp.com/auth",
        new
        {
            Username = username,
            Password = password
        });

    return response.IsSuccessStatusCode;
}

// New Identity Service
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/auth", (LoginRequest request) =>
{
    if (request.Username == "admin" &&
        request.Password == "1234")
    {
        return Results.Ok();
    }

    return Results.Unauthorized();
});

app.Run();

record LoginRequest(string Username, string Password);

Benefits

• Authentication modernized independently
• Easier security updates
• Legacy system remains operational

3. Migrating Monolith Features into Microservices

A reporting module is extracted from a monolith into a dedicated microservice.

// Legacy Monolith
public class ReportService
{
    public string GenerateReport()
    {
        return "Legacy Report";
    }
}

// New Reporting Microservice
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/reports", () =>
{
    return Results.Ok(new
    {
        Report = "Modern Reporting Service"
    });
});

app.Run();

// Gateway Redirect Logic
app.MapGet("/legacy/reports", async () =>
{
    using var client = new HttpClient();

    var response = await client.GetStringAsync(
        "http://localhost:7000/reports");

    return response;
});

Benefits

• Feature-by-feature modernization
• Easier scalability
• Independent deployments

Advantages of Using Strangler Fig Pattern in C#

1. Reduced Migration Risk: Migration happens gradually instead of all at once.

2. Continuous System Availability: The old system remains operational during modernization.

3. Easier Rollback: Problems can be isolated to newly migrated components.

4. Incremental Modernization: Teams can modernize one module at a time.

5. Faster Business Delivery: New features can be released during migration.

6. Better Testing Opportunities: Each migrated component can be independently validated.

7. Supports Cloud Adoption: Allows gradual movement to containers, Kubernetes, or microservices.

8. Lower Initial Cost: Avoids the huge upfront investment of a complete rewrite.

Disadvantages (Weak Points) of Strangler Fig Pattern in C#

1. Temporary Architectural Complexity: Old and new systems coexist simultaneously.

2. Routing Complexity: Traffic routing between systems can become difficult.

3. Duplicate Functionality Risk: Some business logic may temporarily exist in both systems.

4. Longer Migration Timeline: Migration may take months or years.

5. Data Synchronization Challenges: Keeping old and new databases consistent can be difficult.

6. Increased Operational Overhead: Multiple systems must be monitored and maintained.

7. Integration Complexity: Communication between old and new services can become complicated.

8. Legacy Dependencies May Persist: Some outdated components can remain longer than expected.

Strangler Fig Pattern vs Similar Patterns

Pattern Main Purpose Migration Style Risk Level Typical Use Cases Difference from Strangler Fig
Strangler Fig Gradually replace legacy systems Incremental Low Legacy modernization Old and new systems coexist during migration
Big Bang Rewrite Replace entire system at once Full replacement High Small/simple systems Requires complete cutover in one release
Branch by Abstraction Refactor internals safely Incremental code replacement Medium Large refactoring projects Focuses more on internal code structure
Facade Pattern Simplify complex subsystems No migration focus Low API simplification Provides abstraction instead of replacement strategy
Adapter Pattern Make incompatible interfaces work together Compatibility-oriented Low Legacy integration Focuses on interoperability rather than migration
Sidecar Pattern Add supporting infrastructure services Companion service model Low Logging, monitoring, security Enhances services rather than replacing legacy systems

Summary

The Strangler Fig pattern in C# is one of the most effective approaches for modernizing large legacy systems with minimal business risk. Instead of replacing everything at once, teams gradually move functionality from old applications into modern ASP.NET Core services or microservices. This strategy enables continuous delivery, safer deployments, easier testing, and lower migration risk. Although it introduces temporary architectural complexity, it provides a practical and scalable path for enterprise modernization projects.