MakisDSL

DSL Syntax

Complete reference for MakisDSL syntax, patterns, and language constructs.

🔗 Overview

MakisDSL provides a Scala 3 DSL for defining cloud infrastructure. The DSL is designed to be:

🔗 Basic Syntax

Required Imports

All MakisDSL programs start with these imports:

import cloud.*
import cloud.CloudProvider.*
import cloud.syntax.*

cloudApp Function

The entry point for defining infrastructure is the cloudApp function:

val myApp = cloudApp(provider = AWS) {
  // Resource definitions go here
}

Supported Providers

Block Structure

The cloudApp block contains resource definitions and their relationships:

val myApp = cloudApp(provider = AWS) {
  // 1. Define resources
  val storage = objectStorage("my-bucket")
  val compute = serverlessFunction("my-function")
  val database = noSqlTable("my-table")
  
  // 2. Configure resources
  storage.withVersioning(true)
  compute.withRuntime("nodejs18.x")
  database.withHashKey("id", "S")
  
  // 3. Build configured resources
  compute.build
  database.build
}

🔗 Resource Definitions

Resources are the building blocks of your infrastructure. Each resource type has a specific constructor function:

Storage Resources

// Object storage (S3, Blob Storage, Cloud Storage)
val bucket = objectStorage("bucket-name")

Compute Resources

// Serverless functions (Lambda, Azure Functions, Cloud Functions)
val function = serverlessFunction("function-name")

Database Resources

// NoSQL databases (DynamoDB, Cosmos DB, Firestore)
val table = noSqlTable("table-name")

Networking Resources

// Virtual networks
val network = virtualNetwork("network-name")

// Security groups  
val sg = securityGroup("security-group-name")

// Application load balancers
val alb = applicationLoadBalancer("load-balancer-name")

🔗 Builder Patterns

Resources use fluent builder patterns for configuration. Each resource type has specific configuration methods:

Object Storage Configuration

val storage = objectStorage("my-data-bucket")
  .withVersioning(true)
  .withPublicAccess(true)
{
  "my-data-bucket" : {
    "Type" : "AWS::S3::Bucket",
    "Properties" : {
      "VersioningConfiguration" : {
        "Status" : "Enabled"
      },
      "PublicAccessBlockConfiguration" : {
        "BlockPublicAcls" : true,
        "BlockPublicPolicy" : true,
        "IgnorePublicAcls" : true,
        "RestrictPublicBuckets" : true
      }
    }
  }
}
{
  "type" : "Microsoft.Storage/storageAccounts",
  "apiVersion" : "2023-01-01",
  "name" : "my-data-bucket",
  "location" : "[resourceGroup().location]",
  "properties" : {
    "sku" : {
      "name" : "Standard_LRS"
    },
    "kind" : "StorageV2",
    "accessTier" : "Hot",
    "isVersioningEnabled" : true
  }
}

Serverless Function Configuration

val function = serverlessFunction("my-api-handler")
  .withRuntime("nodejs18.x")
  .withHandler("index.handler")
  .withCode("exports.handler = async (event) => { return { statusCode: 200, body: 'Hello World!' }; }")
  .build
{
  "my-api-handler" : {
    "Type" : "AWS::Lambda::Function",
    "Properties" : {
      "Runtime" : "nodejs18.x",
      "Handler" : "index.handler",
      "Code" : {
        "ZipFile" : "exports.handler = async (event) => { return { statusCode: 200, body: 'Hello World!' }; };"
      }
    }
  }
}
{
  "type" : "Microsoft.Web/sites",
  "apiVersion" : "2023-01-01",
  "name" : "my-api-handler",
  "location" : "[resourceGroup().location]",
  "properties" : {
    "kind" : "functionapp",
    "siteConfig" : {
      "appSettings" : [ {
        "name" : "FUNCTIONS_WORKER_RUNTIME",
        "value" : "node"
      }, {
        "name" : "WEBSITE_NODE_DEFAULT_VERSION",
        "value" : "~18"
      } ]
    }
  }
}

NoSQL Table Configuration

val table = noSqlTable("my-users-table")
  .withHashKey("userId", "S")
  .build
{
  "my-users-table" : {
    "Type" : "AWS::DynamoDB::Table",
    "Properties" : {
      "KeySchema" : [ {
        "AttributeName" : "userId",
        "KeyType" : "HASH"
      } ],
      "AttributeDefinitions" : [ {
        "AttributeName" : "userId",
        "AttributeType" : "S"
      } ],
      "BillingMode" : "PAY_PER_REQUEST"
    }
  }
}
{
  "type" : "Microsoft.DocumentDB/databaseAccounts",
  "apiVersion" : "2023-04-15",
  "name" : "my-users-table",
  "location" : "[resourceGroup().location]",
  "properties" : {
    "databaseAccountOfferType" : "Standard",
    "consistencyPolicy" : {
      "defaultConsistencyLevel" : "Session"
    },
    "locations" : [ {
      "locationName" : "[resourceGroup().location]",
      "failoverPriority" : 0
    } ]
  }
}

🔗 Dependencies & References

MakisDSL provides explicit dependency management and resource referencing:

Resource Dependencies

Use .dependsOn() to create explicit dependencies:

val bucket = objectStorage("code-bucket")
  .withVersioning(true)

val function = serverlessFunction("processor")
  .withRuntime("nodejs18.x")
  .withHandler("index.handler")
  .dependsOn(bucket)  // Function depends on bucket
  .build

val table = noSqlTable("results")
  .withHashKey("id", "S")
  .dependsOn(function)  // Table depends on function
  .build

Resource References

Use .reference to reference other resources in configuration:

val bucket = objectStorage("deployment-artifacts")
  .withVersioning(true)

val function = serverlessFunction("api-handler")
  .withRuntime("nodejs18.x")
  .withHandler("index.handler")
  .withCode(bucket.reference)  // Reference bucket for code location
  .dependsOn(bucket)
  .build

Complete Example with Dependencies

val myApp = cloudApp(provider = AWS) {
  val bucket = objectStorage("my-code-bucket")
    .withVersioning(true)

  val function = serverlessFunction("my-api-handler")
    .withRuntime("nodejs18.x")
    .withHandler("index.handler")
    .withCode(bucket.reference)
    .dependsOn(bucket)
    .build

  val table = noSqlTable("my-users-table")
    .withHashKey("userId", "S")
    .dependsOn(function)
    .build
}
{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Resources" : {
    "my-code-bucket" : {
      "Type" : "AWS::S3::Bucket",
      "Properties" : {
        "VersioningConfiguration" : {
          "Status" : "Enabled"
        }
      }
    },
    "my-api-handler" : {
      "Type" : "AWS::Lambda::Function",
      "Properties" : {
        "Runtime" : "nodejs18.x",
        "Handler" : "index.handler",
        "Code" : {
          "S3Bucket" : {
            "Ref" : "my-code-bucket"
          }
        }
      },
      "DependsOn" : [ "my-code-bucket" ]
    },
    "my-users-table" : {
      "Type" : "AWS::DynamoDB::Table",
      "Properties" : {
        "KeySchema" : [ {
          "AttributeName" : "userId",
          "KeyType" : "HASH"
        } ],
        "AttributeDefinitions" : [ {
          "AttributeName" : "userId",
          "AttributeType" : "S"
        } ],
        "BillingMode" : "PAY_PER_REQUEST"
      },
      "DependsOn" : [ "my-api-handler" ]
    }
  }
}
{
  "resources" : [ {
    "name" : "my-code-bucket",
    "type" : "storage.v1.bucket",
    "properties" : {
      "location" : "US",
      "storageClass" : "STANDARD",
      "versioning" : {
        "enabled" : true
      }
    }
  }, {
    "name" : "my-api-handler",
    "type" : "cloudfunctions.v1.function",
    "properties" : {
      "location" : "us-central1",
      "runtime" : "nodejs18",
      "entryPoint" : "index",
      "sourceArchiveUrl" : "gs://my-source-bucket/function.zip"
    },
    "metadata" : {
      "dependsOn" : [ "my-code-bucket" ]
    }
  }, {
    "name" : "my-users-table",
    "type" : "firestore.v1.database",
    "properties" : {
      "location" : "us-central1",
      "type" : "FIRESTORE_NATIVE"
    },
    "metadata" : {
      "dependsOn" : [ "my-api-handler" ]
    }
  } ]
}

🔗 Environment Variables

MakisDSL supports compile-time environment variable injection using macros:

Basic Environment Variables

import cloud.syntax.env

val storage = objectStorage(env("TEST_BUCKET_NAME", "default-bucket"))
  .withVersioning(true)

val function = serverlessFunction(s"${env("TEST_ENV", "dev")}-api-handler")
  .withRuntime("nodejs18.x")
  .withHandler("index.handler")
  .build

val table = noSqlTable(s"${env("TEST_ENV", "dev")}-users-table")
  .withHashKey("userId", "S")
  .build

Compile-time Validation

Environment variables are resolved at compile-time. Missing required variables will cause compilation errors:

// This will fail at compile-time if DATABASE_URL is not set
val dbUrl = env("DATABASE_URL")

// This will use the default if not set
val bucketName = env("BUCKET_NAME", "default-bucket")

🔗 Extension Methods

MakisDSL provides extension methods for cleaner syntax and additional functionality:

Object Storage Extensions

val storage = objectStorage("my-bucket")
  .withVersioning(true)      // Enable versioning
  .withPublicAccess(false)   // Block public access

Security Group Extensions

val sg = securityGroup("web-sg")
  .allowInbound("HTTP", 80)
  .allowInbound("HTTPS", 443)

Application Load Balancer Extensions

val function = serverlessFunction("api-handler")
  .withRuntime("nodejs18.x")
  .withHandler("index.handler")
  .build

val alb = applicationLoadBalancer("web-alb")
  .withTargets(function)

Multi-Cloud Example

// Same DSL works for all providers
def createApp(provider: CloudProvider) = {
  cloudApp(provider = provider) {
    val bucket = objectStorage("my-data-bucket")
      .withVersioning(true)

    val function = serverlessFunction("my-api-handler")
      .withRuntime("nodejs18.x")
      .withHandler("index.handler")
      .withCode(bucket.reference)
      .dependsOn(bucket)
      .build

    val table = noSqlTable("my-users-table")
      .withHashKey("userId", "S")
      .dependsOn(function)
  }
}

val awsApp = createApp(AWS)
val azureApp = createApp(Azure)
val gcpApp = createApp(GCP)
// AWS::S3::Bucket, AWS::Lambda::Function, AWS::DynamoDB::Table
{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Resources" : {
    "my-data-bucket" : {
      "Type" : "AWS::S3::Bucket"
    },
    "my-api-handler" : {
      "Type" : "AWS::Lambda::Function"
    },
    "my-users-table" : {
      "Type" : "AWS::DynamoDB::Table"
    }
  }
}
// Microsoft.Storage, Microsoft.Web, Microsoft.DocumentDB
{
  "$schema" : "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion" : "1.0.0.0",
  "resources" : [ {
    "type" : "Microsoft.Storage/storageAccounts"
  }, {
    "type" : "Microsoft.Web/sites"
  }, {
    "type" : "Microsoft.DocumentDB/databaseAccounts"
  } ]
}
// storage.v1.bucket, cloudfunctions.v1.function, firestore.v1.database
{
  "resources" : [ {
    "type" : "storage.v1.bucket"
  }, {
    "type" : "cloudfunctions.v1.function"
  }, {
    "type" : "firestore.v1.database"
  } ]
}