Using the Strangler Fig Pattern in SST

Wed, 11 Jan 2023

#sst #aws #pattern

I am currently working on the architecture of a new project for my company. This new project must be compatible with an historical monolith, then extend it. It's a common case to rewrite an existing application with modern toolkit.

What is the Strangler Fit Pattern?

Martin Fowler was describing in a article back in 2004 a way to slowly replace a monolith by a new product: the Strangler Fig Pattern. The main advantage: a slow migration, one endpoint by one endpoint, not a bigbang. This pattern is actually widely used when refactoring an application to micro-services for example.

Pasted image 20230111172741.png
From Microsoft Learn

Why in SST (Serverless Stack)?

SST is a serverless framework that help you to build fullstack application. On my new project, I want to take advantage of this framework (it's a classic piece of our toolkit nowadays!), but I want to replace an application, not to create a new one. So the Strangler Fit Pattern seems pretty attracting.

Architecture

Based on a AWS reInvent presentation (Youtube video link here), I was convinced to use an API Gateway with the ANY /{proxy+} to facade the application:

Pasted image 20230111173023.png
From AWS Presentation, Migrating monolithic applications with the strangler pattern

Starting from there I have tried to simulate a monolith application (represented by a ApplicationLoadBalancedFargateService CDK construct in my project). The implementation of the monolith is not very important, I just rely on the Application Load Balancer (there is 99% chance you have such service if your monolith is hosted on AWS, either on EC2 or ECS!).

Here is my monolith in CDK:

  const loadBalancedFargateService = new ApplicationLoadBalancedFargateService(
    stack,
    "Service",
    {
      propagateTags: PropagatedTagSource.SERVICE,
      memoryLimitMiB: 1024,
      desiredCount: 1,
      cpu: 512,
      taskImageOptions: {
        image: ContainerImage.fromRegistry("nginxdemos/hello"),
      },
      publicLoadBalancer: false,
    }
  );

For the demo, it's using a very simple docker image, nginxdemos/hello

Then SST is allowing me to use the same {proxy+} in the API routes:

  const api = new Api(stack, "api", {
    routes: {
      "GET /": "functions/lambda.handler",
      "ANY /{proxy+}": {
        type: "alb",
        cdk: {
          albListener: loadBalancedFargateService.listener,
        },
      },
    },
  });

And voilà, I can build new routes with SST, and all routes not specified in routes will be handled by the old application declared in the ApplicationLoadBalancedFargateService.

The route / is answered by my lambda.handler function:
Pasted image 20230111174021.png

Any other routes are answered by my monolith:
Pasted image 20230111174103.png

To Conclude

This was a quick one but I think it can be a very efficient way to move slowly to SST framework! You can find the example repository on GitHub here. Happy tests with this pattern on modern stack, or chat with me on Twitter!