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:
- Type-safe - Compile-time validation of resource configurations
- Provider-agnostic - Same syntax works across AWS, Azure, and GCP
- Composable - Resources can reference and depend on each other
- Fluent - Builder patterns for clean, readable configuration
🔗 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
AWS
- Amazon Web ServicesAzure
- Microsoft AzureGCP
- Google Cloud Platform
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"
} ]
}