Skip to main content

Overview

The ComputeFactory is Fjall’s pattern for creating compute resources. It provides a unified interface to deploy different compute types including ECS containers, EC2 instances, and Lambda functions.

Basic Usage

import { App, ComputeFactory } from "@fjall/components-infrastructure";

const app = App.getApp("myapp");

app.addCompute(
  ComputeFactory.build("MyCompute", {
    type: "ecs",
    services: [
      {
        name: "web",
        capacityProvider: "FARGATE",
        cpu: 256,
        memoryLimitMiB: 512,
      },
    ],
  }),
);

Compute Types

ECS (Elastic Container Service)

Deploy containerised applications with three variants:
app.addCompute(
  ComputeFactory.build("MyService", {
    type: "ecs",
    services: [
      {
        name: "api",
        capacityProvider: "FARGATE",
        cpu: 512,
        memoryLimitMiB: 1024,
        desiredCount: 2,
      },
    ],
  }),
);

Fargate Spot (Cost Savings)

app.addCompute(
  ComputeFactory.build("BatchProcessor", {
    type: "ecs",
    services: [
      {
        name: "worker",
        capacityProvider: "FARGATE_SPOT",
        cpu: 256,
        memoryLimitMiB: 512,
        // Up to 70% cost savings
      },
    ],
  }),
);

EC2-backed ECS

app.addCompute(
  ComputeFactory.build("DevService", {
    type: "ecs",
    services: [
      {
        name: "dev",
        capacityProvider: "EC2",
        cpu: 256,
        memoryLimitMiB: 512,
      },
    ],
  }),
);

Lambda Functions

Deploy serverless functions with code deployment:
app.addCompute(
  ComputeFactory.build("MyFunction", {
    type: "lambda",
    deployment: "code", // Required discriminator
    handler: "index.handler",
    memorySize: 512,
    timeout: 30,
  }),
);
With container images:
import { Repository } from "aws-cdk-lib/aws-ecr";

// Reference an existing ECR repository
const ecrRepo = Repository.fromRepositoryName(scope, "FunctionRepo", "my-repo");

app.addCompute(
  ComputeFactory.build("ContainerFunction", {
    type: "lambda",
    deployment: "container", // Required discriminator
    ecrRepository: ecrRepo, // Required for container deployment
    memorySize: 1024,
  }),
);
Container-based Lambdas require an ECR repository. The ecrRepository parameter is required when deployment: "container". When using Fjall’s App scaffolding, ECR repositories are created automatically.

EC2 Instances

Deploy traditional virtual machines:
app.addCompute(
  ComputeFactory.build("MyInstance", {
    type: "ec2",
    instanceType: "t4g.micro",
    ssh: {},
    minCapacity: 1,
    maxCapacity: 3,
  }),
);

Configuration Parameters

Common Parameters

ParameterTypeDescriptionDefault
type"ecs" | "lambda" | "ec2"Compute type"ecs"
vpcIVpcVPC to deploy intoApp default VPC

Connection Syntax

Connect compute resources to databases, storage, and queues:
// Simple connection (default access)
const api = app.addCompute(
  ComputeFactory.build("API", {
    type: "ecs",
    services: [
      {
        name: "api",
        capacityProvider: "FARGATE",
        containers: [
          {
            environment: {
              DATABASE_HOST: database.getHostEndpoint(),
            },
          },
        ],
      },
    ],
  }),
);

ECS Parameters

ParameterTypeDescriptionDefault
servicesarrayService configurations (see below)Required

Service Configuration

Each service in the services array:
ParameterTypeDescriptionDefault
namestringService nameRequired
capacityProvider"FARGATE" | "FARGATE_SPOT" | "EC2"Compute capacityRequired
cpunumberCPU units (256, 512, 1024, etc.)256
memoryLimitMiBnumberMemory in MiB512
desiredCountnumberNumber of tasks1
containersarrayContainer definitions (see below)Required
routingobjectALB routing configuration-
scalingobjectAuto-scaling configuration-
dockerfilePathstringPath to Dockerfile"Dockerfile"
dockerTargetstringDocker build target stage-
ssmSecretsPathstringSSM Parameter Store path prefix-
taskRoleInlinePoliciesobjectCustom IAM policies-

Container Configuration

Each container in the containers array:
ParameterTypeDescriptionDefault
namestringContainer nameService name
portnumberContainer port80
imagestringContainer image (if not building)-
environmentobjectEnvironment variables{}
secretsImportobjectSecrets from Secrets Manager{}
commandstring[]Container command-
healthCheckobjectHealth check configuration-

Routing Configuration

ParameterTypeDescriptionDefault
pathstringURL path pattern"/*"
hoststringHost header match-
prioritynumberRule priorityAuto
healthCheckPathstringHealth check endpoint"/"

Scaling Configuration

ParameterTypeDescriptionDefault
minCapacitynumberMinimum tasks1
maxCapacitynumberMaximum tasks1
scalingTypeScalingTypeScaling metric (ScalingType.CPU or ScalingType.MEMORY)ScalingType.CPU
scalingType takes the ScalingType enum, not a string. Import it from @fjall/components-infrastructure:
import { ScalingType } from "@fjall/components-infrastructure";

scaling: {
  minCapacity: 2,
  maxCapacity: 10,
  scalingType: ScalingType.CPU,
}

ECS Cluster Configuration

Configure the ECS cluster at the root level of the ComputeFactory:
ParameterTypeDescriptionDefault
ecrRepositoryIRepositoryECR repository for container imagesApp default registry
clusterobjectCluster-level configuration-
cluster.loadBalancer"public" | "internal" | falseLoad balancer type"public"
cluster.directAccessbooleanEnable direct container access (no ALB)false
cluster.domainstringCustom domain for the cluster ALB-
cluster.domainConfigobjectDomain configuration options-
cluster.domainConfig.certificateArnstringACM certificate ARN-
cluster.domainConfig.hostedZoneIdstringRoute 53 hosted zone ID-
cluster.domainConfig.hostedZoneNamestringRoute 53 hosted zone name-

Load Balancer Options

// Public load balancer (default) - internet-facing
app.addCompute(
  ComputeFactory.build("PublicAPI", {
    type: "ecs",
    cluster: {
      loadBalancer: "public",
    },
    services: [
      {
        name: "api",
        capacityProvider: "FARGATE",
        containers: [{ port: 3000 }],
      },
    ],
  }),
);

// Internal load balancer - VPC only
app.addCompute(
  ComputeFactory.build("InternalAPI", {
    type: "ecs",
    cluster: {
      loadBalancer: "internal",
    },
    services: [
      {
        name: "api",
        capacityProvider: "FARGATE",
        containers: [{ port: 3000 }],
      },
    ],
  }),
);

// No load balancer - direct container access
app.addCompute(
  ComputeFactory.build("Worker", {
    type: "ecs",
    cluster: {
      loadBalancer: false,
      directAccess: true,
    },
    services: [
      {
        name: "worker",
        capacityProvider: "FARGATE",
        containers: [{ port: 3000 }],
      },
    ],
  }),
);

Custom Domain Configuration

app.addCompute(
  ComputeFactory.build("WebApp", {
    type: "ecs",
    cluster: {
      loadBalancer: "public",
      domain: "api.example.com",
      domainConfig: {
        certificateArn: "arn:aws:acm:us-east-1:123456789:certificate/abc-123",
        hostedZoneId: "Z1234567890ABC",
        hostedZoneName: "example.com",
      },
    },
    services: [
      {
        name: "web",
        capacityProvider: "FARGATE",
        containers: [{ port: 3000 }],
      },
    ],
  }),
);

EC2 Capacity Provider Configuration

When using capacityProvider: "EC2", additional EC2-specific options are available:
ParameterTypeDescriptionDefault
services[].ec2ConfigobjectEC2-specific configuration-
services[].ec2Config.instanceTypeInstanceTypeEC2 instance typet4g.medium
services[].ec2Config.minCapacitynumberMinimum EC2 instances1
services[].ec2Config.maxCapacitynumberMaximum EC2 instances3
services[].ec2Config.spotCapacityPercentagenumberPercentage of spot instances (0-100)0
services[].ec2Config.sshSshConfig | falseSSH access configurationfalse
import { InstanceType } from "aws-cdk-lib/aws-ec2";

app.addCompute(
  ComputeFactory.build("EC2Service", {
    type: "ecs",
    services: [
      {
        name: "app",
        capacityProvider: "EC2",
        cpu: 512,
        memoryLimitMiB: 1024,
        ec2Config: {
          instanceType: new InstanceType("t4g.large"),
          minCapacity: 2,
          maxCapacity: 10,
          spotCapacityPercentage: 50, // 50% spot for cost savings
          ssh: {},
        },
      },
    ],
  }),
);

Multi-Container Service Example

import { ScalingType } from "@fjall/components-infrastructure";

app.addCompute(
  ComputeFactory.build("WebApp", {
    type: "ecs",
    services: [
      {
        name: "web",
        capacityProvider: "FARGATE",
        cpu: 512,
        memoryLimitMiB: 1024,
        containers: [
          {
            name: "app",
            port: 3000,
            environment: { NODE_ENV: "production" },
          },
          {
            name: "nginx",
            image: "nginx:alpine",
            port: 80,
          },
        ],
        routing: {
          path: "/*",
          healthCheckPath: "/health",
        },
        scaling: {
          minCapacity: 2,
          maxCapacity: 10,
          scalingType: ScalingType.CPU,
        },
      },
    ],
  }),
);

Lambda Parameters

ParameterTypeDescriptionDefault
deployment"code" | "container"Deployment type (required)Required
handlerstringFunction handler (code deployment)"index.handler"
architectureArchitectureCPU architecture (Architecture.ARM_64 or Architecture.X86_64)Architecture.ARM_64
timeoutnumberTimeout in seconds3
memorySizenumberMemory in MB128
ephemeralStorageSizenumberEphemeral storage in MB (512-10240)512
environmentobjectEnvironment variables{}
functionUrlobject | falseFunction URL configuration-
functionUrl.authTypeFunctionUrlAuthTypeAuth type (NONE, AWS_IAM)-
functionUrl.corsobjectCORS configuration-
ssmSecretsPathstringSSM Parameter Store path prefix for secrets-
inlinePolicyPolicyStatement[]Custom IAM policy statements-
descriptionstringFunction description-
architecture takes the Architecture enum from aws-cdk-lib/aws-lambda, not a string. Use Architecture.ARM_64 or Architecture.X86_64.

EC2 Parameters

ParameterTypeDescriptionDefault
instanceTypestringEC2 instance type"t4g.micro"
sshSshConfig | falseSSH access configurationfalse
userDataUserDataInstance startup script-
machineImageIMachineImageAMI to useLatest Amazon Linux
minCapacitynumberMin instances in ASG1
maxCapacitynumberMax instances in ASG1

Database Connections

Connect compute resources to databases:
const database = app.addDatabase(
  DatabaseFactory.build("MyDatabase", {
    type: "Aurora",
    databaseName: "mydb",
  }),
);

app.addCompute(
  ComputeFactory.build("MyAPI", {
    type: "ecs",
    services: [
      {
        name: "api",
        capacityProvider: "FARGATE",
        containers: [
          {
            environment: {
              DATABASE_HOST: database.getHostEndpoint(),
              DATABASE_NAME: database.getDatabaseName(),
            },
            secretsImport: {
              DATABASE_PASSWORD: database
                .getCredentials()
                .getImport("password"),
            },
          },
        ],
      },
    ],
  }),
);

Lambda Function URLs

Make Lambda functions publicly accessible:
import { FunctionUrlAuthType } from "aws-cdk-lib/aws-lambda";
import { Duration } from "aws-cdk-lib";

app.addCompute(
  ComputeFactory.build("PublicAPI", {
    type: "lambda",
    deployment: "code",
    handler: "index.handler",
    functionUrl: {
      authType: FunctionUrlAuthType.NONE,
      cors: {
        allowedOrigins: ["*"],
        allowedMethods: ["GET", "POST"],
        allowedHeaders: ["Content-Type"],
        maxAge: Duration.hours(24),
      },
    },
  }),
);

Auto-Scaling

ECS services support auto-scaling:
app.addCompute(
  ComputeFactory.build("ScalableService", {
    type: "ecs",
    services: [
      {
        name: "api",
        capacityProvider: "FARGATE",
        desiredCount: 2,
        scaling: {
          minCapacity: 2,
          maxCapacity: 10,
        },
      },
    ],
  }),
);

Custom Domains

Configure custom domains for ECS services:
app.addCompute(
  ComputeFactory.build("WebApp", {
    type: "ecs",
    cluster: {
      domain: "app.example.com",
      domainConfig: {
        certificateArn: "arn:aws:acm:us-east-1:123456789:certificate/abc-123",
      },
    },
    services: [
      {
        name: "web",
        capacityProvider: "FARGATE",
        containers: [{ port: 3000 }],
      },
    ],
  }),
);

Cost Optimisation

Development Environments

Use Fargate Spot for cost savings:
// Fargate Spot for dev/test
app.addCompute(
  ComputeFactory.build("DevApp", {
    type: "ecs",
    services: [
      {
        name: "dev",
        capacityProvider: "FARGATE_SPOT",
        cpu: 256,
        memoryLimitMiB: 512,
      },
    ],
  }),
);

// Lambda for batch jobs
app.addCompute(
  ComputeFactory.build("BatchJob", {
    type: "lambda",
    deployment: "code",
    handler: "batch.handler",
    timeout: 900,
    memorySize: 1024,
  }),
);

Production Environments

Right-size resources:
app.addCompute(
  ComputeFactory.build("ProdAPI", {
    type: "ecs",
    services: [
      {
        name: "api",
        capacityProvider: "FARGATE",
        cpu: 512,
        memoryLimitMiB: 1024,
        desiredCount: 2,
        scaling: {
          minCapacity: 2,
          maxCapacity: 10,
        },
      },
    ],
  }),
);

Next Steps

Storage Factory

Create S3 buckets and storage resources.

Database Factory

Provision Aurora, RDS, DynamoDB, and ClickHouse databases.

Standard Pattern

Compose full applications from these factories.

ECS Cluster Resources

Configure ECS clusters for advanced workloads.