5. Cloud-basierte AWS SaaS Plattform

5.1. AWS Allgemein

Amazon Web Services bildet das Fundament für moderne, skalierbare Multi-Tenant-SaaS-Anwendungen und bietet alle notwendigen Services für eine vollständig serverlose Architektur.

Projekt-Architektur Überblick

Unsere Multi-Tenant-SaaS-Plattform folgt einer durchdachten, mehrschichtigen Architektur, die optimale Skalierbarkeit, Sicherheit und Tenant-Isolation gewährleistet:

┌───────────────────────────────────────────────────────────────────┐
│                    🌐 FRONTEND BENUTZEROBERFLÄCHE                 │
│                                                                   │
│  React + TypeScript Anwendung                                    │
│  • Responsive Design                                             │
│  • Multi-Tenant Login                                            │
│  • Benutzer-Dashboard                                            │
│                                                                   │
│  Gehostet auf: CloudFront CDN + S3                               │
└───────────────────────────┬───────────────────────────────────────┘
                            │
                            │ HTTPS Anfragen
                            ▼
┌───────────────────────────────────────────────────────────────────┐
│                        🚪 API GATEWAY                             │
│                                                                   │
│  • Custom JWT Authorizers                                        │
│  • Multi-Tenant Routing                                          │
│  • Rate Limiting                                                 │
│  • Request/Response Transformation                               │
└───────────────────────────┬───────────────────────────────────────┘
                            │
                            │ Ruft auf
                            ▼
┌───────────────────────────────────────────────────────────────────┐
│                    ⚡ AWS LAMBDA FUNKTIONEN                       │
│                                                                   │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌──────────┐ │
│  │    🔐       │  │    🏢       │  │    👥       │  │    📋    │ │
│  │ Authentif.  │  │  Mandanten  │  │ Benutzer    │  │Ressourcen│ │
│  │ Funktionen  │  │ Verwaltung  │  │ Verwaltung  │  │Verwaltung│ │
│  └─────────────┘  └─────────────┘  └─────────────┘  └──────────┘ │
└───────────────────────────┬───────────────────────────────────────┘
                            │
                            │ Datenbankverbindungen
                            ▼
┌───────────────────────────────────────────────────────────────────┐
│                🗄️ AURORA SERVERLESS POSTGRESQL                    │
│                                                                   │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐   │
│  │  📊 Gemeinsam   │  │  🏢 Mandant 1   │  │  🏢 Mandant N   │   │
│  │     Schema      │  │     Schema      │  │     Schema      │   │
│  │                 │  │                 │  │                 │   │
│  │ • mandanten     │  │ • benutzer      │  │ • benutzer      │   │
│  │ • analytics     │  │ • ressourcen    │  │ • ressourcen    │   │
│  │ • einstellungen │  │ • logs          │  │ • logs          │   │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘   │
└───────────────────────────────────────────────────────────────────┘

Unterstützende Services:

┌───────────────────────────────────────────────────────────────────┐
│                     🔧 UNTERSTÜTZENDE SERVICES                    │
│                                                                   │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐   │
│  │  🔑 Amazon      │  │  📡 EventBridge │  │  📊 CloudWatch  │   │
│  │     Cognito     │  │                 │  │                 │   │
│  │                 │  │ • Event Routing │  │ • Monitoring    │   │
│  │ • Benutzer-Pools│  │ • Service       │  │ • Logging       │   │
│  │ • Authentifikation│ │   Kommunikation │  │ • Metriken      │   │
│  │ • JWT Tokens    │  │ • Workflows     │  │ • Alarme        │   │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘   │
└───────────────────────────────────────────────────────────────────┘

Diese Architektur implementiert bewährte Multi-Tenancy-Patterns und ermöglicht eine klare Trennung zwischen den verschiedenen Schichten, während sie gleichzeitig optimale Performance und Skalierbarkeit gewährleistet.

_images/Home.png

Dieses Kapitel zeigt die Implementierung einer serverless AWS-Architektur für Multi-Tenant SaaS-Anwendungen.

5.1.1. Service-Portfolio und Architektur-Patterns

AWS bietet eine umfassende Palette von Services, die speziell für moderne SaaS-Anwendungen optimiert sind. Die Kombination aus serverlosen Services ermöglicht eine hochskalierbare und kosteneffiziente Architektur.

Core Services für SaaS-Anwendungen:

  • AWS Lambda: Serverlose Compute-Engine für Business Logic ohne Server-Management

  • Amazon Aurora Serverless v2: Automatisch skalierender PostgreSQL-Service mit Data API

  • Amazon API Gateway: Vollständig verwalteter Service für REST APIs mit integrierter Authentifizierung

  • Amazon Cognito: Benutzer- und Identitätsverwaltung mit Multi-Tenant-Unterstützung

  • Amazon EventBridge: Event-driven Architektur für lose gekoppelte Services

  • Amazon S3: Objektspeicher für Static Web Hosting und Dateiverwaltung

// Beispiel: Service-Integration in unserem SaaS-Stack
export class ApiStack extends BaseStack {
    constructor(scope: Construct, id: string, props: ApiStackProps) {
        super(scope, id, props);

        // API Gateway mit CORS und Logging
        this.api = new apigateway.RestApi(this, 'TenantAPI', {
            restApiName: `${props.envName}-tenant-api`,
            description: 'Multi-tenant API Gateway',
            defaultCorsPreflightOptions: {
                allowOrigins: apigateway.Cors.ALL_ORIGINS,
                allowMethods: apigateway.Cors.ALL_METHODS,
                allowHeaders: ['Content-Type', 'Authorization', 'X-Tenant-ID'],
            },
            deployOptions: {
                stageName: props.envConfig.stage,
                loggingLevel: apigateway.MethodLoggingLevel.INFO,
                dataTraceEnabled: true,
            },
        });
    }
}

5.1.2. Multi-Tenant Patterns und Isolationsstrategien

Multi-Tenancy erfordert eine durchdachte Balance zwischen Ressourcen-Sharing und Daten-Isolation. Unsere Implementierung verwendet ein hybrides Schema-per-Tenant-Modell für optimale Isolation bei kosteneffizienter Ressourcennutzung.

Tenant-Isolation Patterns:

  • Schema-per-Tenant: Jeder Tenant erhält ein eigenes Datenbank-Schema für komplette Daten-Isolation

  • Shared Infrastructure: Gemeinsame Lambda-Funktionen und API Gateway reduzieren Kosten

  • Context-aware Authorization: Tenant-Kontext wird über Custom Authorizer in jeden Request eingebettet

// Implementierung: Tenant Schema-Erstellung
async function createTenantSchema(tenantId: string): Promise<void> {
    const schemaName = `tenant_${tenantId}`;

    // Schema erstellen
    await rdsClient.send(new ExecuteStatementCommand({
        resourceArn: clusterArn,
        secretArn: secretArn,
        database: database,
        sql: `CREATE SCHEMA IF NOT EXISTS ${schemaName}`
    }));

    // Tenant-spezifische Tabellen
    await rdsClient.send(new ExecuteStatementCommand({
        resourceArn: clusterArn,
        secretArn: secretArn,
        database: database,
        sql: `
            CREATE TABLE IF NOT EXISTS ${schemaName}.users (
                id VARCHAR(36) PRIMARY KEY,
                email VARCHAR(255) NOT NULL,
                firstName VARCHAR(100) NOT NULL,
                lastName VARCHAR(100) NOT NULL,
                role VARCHAR(50) NOT NULL,
                createdAt TIMESTAMP NOT NULL,
                updatedAt TIMESTAMP NOT NULL,
                UNIQUE(email)
            )
        `
    }));
}

Hier wird das Design und die Implementierung einer Multi-Tenant-Datenbankarchitektur mit Aurora Serverless beschrieben.

Dieses Kapitel behandelt die Implementierung eines sicheren Authentication- und Authorization-Systems mit AWS Cognito.

5.1.3. Security Framework und Compliance

Sicherheit ist fundamental für SaaS-Anwendungen und erfordert ein mehrschichtiges Konzept von der Netzwerkebene bis zur Anwendungslogik.

Security-Layers:

  • Network Security: VPC-Isolation mit privaten Subnetzen für kritische Ressourcen

  • Identity Management: IAM-Rollen mit Least-Privilege-Prinzip

  • Data Encryption: End-to-End-Verschlüsselung mit AWS KMS

  • API Security: JWT-Token-Validation mit Custom Claims für Tenant-Kontext

// Security Group Konfiguration für Lambda-to-Database
const lambdaSecurityGroup = new ec2.SecurityGroup(this, 'LambdaSecurityGroup', {
    vpc: props.vpc,
    description: 'Security Group for Lambda Functions',
    allowAllOutbound: true,
});

// Database-Zugriff nur für autorisierte Lambda-Funktionen
this.database.connections.allowFrom(
    lambdaSecurityGroup,
    ec2.Port.tcp(5432),
    'Allow Lambda to connect to the database'
);

5.2. Infrastruktur

Die Infrastruktur-Schicht bildet das robuste Fundament für eine skalierbare Multi-Tenant-SaaS-Anwendung mit durchdachtem Netzwerk-Design und Service-Integration.

5.2.1. VPC Design und Netzwerk-Architektur

Eine gut konzipierte Netzwerk-Architektur ist essentiell für Sicherheit und Performance. Unsere Implementierung verwendet eine Multi-Tier-Architektur mit klar getrennten Zonen.

Netzwerk-Topologie:

  • Public Subnets: Load Balancer und NAT Gateways für Internet-Zugriff

  • Private Subnets: Lambda-Funktionen und Application Logic mit kontrolliertem Outbound-Zugriff

  • Isolated Subnets: Datenbank-Cluster ohne Internet-Verbindung für maximale Sicherheit

// VPC mit Multi-AZ-Deployment für High Availability
this.vpc = new ec2.Vpc(this, 'MultiTenantVPC', {
    maxAzs: 2,
    natGateways: 1,
    subnetConfiguration: [
        {
            cidrMask: 24,
            name: 'Public',
            subnetType: ec2.SubnetType.PUBLIC,
        },
        {
            cidrMask: 24,
            name: 'Private',
            subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
        },
        {
            cidrMask: 24,
            name: 'Isolated',
            subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
        }
    ]
});

// VPC Flow Logs für Netzwerk-Monitoring
new ec2.FlowLog(this, 'FlowLogs', {
    resourceType: ec2.FlowLogResourceType.fromVpc(this.vpc),
    destination: ec2.FlowLogDestination.toCloudWatchLogs(),
});

Hier wird die Verwendung von AWS CDK für Infrastructure as Code und automatisierte Deployments erklärt.

_images/resource_map.png

5.2.2. Aurora Serverless Integration und Database Design

Aurora Serverless v2 bietet die perfekte Balance zwischen Performance und Kosteneffizienz für Multi-Tenant-Anwendungen mit automatischer Skalierung basierend auf der tatsächlichen Nutzung.

Database-Features:

  • Serverless v2: Automatische Skalierung von 0.5 bis 16 ACUs basierend auf Workload

  • Data API: HTTP-basierte Datenbankzugriffe ohne persistente Verbindungen

  • Multi-AZ Deployment: Writer und Reader Instances für High Availability

// Aurora Serverless v2 Cluster mit optimierter Konfiguration
this.database = new rds.DatabaseCluster(this, 'Database', {
    engine: rds.DatabaseClusterEngine.auroraPostgres({
        version: rds.AuroraPostgresEngineVersion.VER_15_4,
    }),
    defaultDatabaseName: 'tenantdb',
    vpc: props.vpc,
    vpcSubnets: {
        subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
    },
    securityGroups: [dbSecurityGroup],
    credentials: rds.Credentials.fromSecret(this.dbCredentials),
    serverlessV2MinCapacity: 0.5,
    serverlessV2MaxCapacity: 16,
    enableDataApi: true,
    writer: rds.ClusterInstance.serverlessV2('writer'),
    readers: [
        rds.ClusterInstance.serverlessV2('reader1', { scaleWithWriter: true }),
    ],
});
_images/Aurora.png

5.2.3. API Gateway Setup und Request-Routing

API Gateway fungiert als zentraler Eingangspoint für alle Client-Requests und ermöglicht eine einheitliche Authentifizierung und Request-Validation.

Gateway-Features:

  • CORS Configuration: Vollständige Cross-Origin-Unterstützung für Web-Clients

  • Custom Authorizer: JWT-Token-Validation mit Tenant-Kontext-Extraktion

  • Request Validation: Schema-basierte Input-Validation vor Lambda-Ausführung.

// Custom Authorizer für Tenant-aware Authentication
const authorizerLambda = new lambda.Function(this, 'AuthorizerLambda', {
    runtime: lambda.Runtime.NODEJS_18_X,
    handler: 'index.handler',
    code: lambda.Code.fromAsset(
        path.join(__dirname, '../../../backend/dist/lambdas/auth/authorizer'),
    ),
    environment: {
        USER_POOL_ID: props.userPool.userPoolId,
        APP_CLIENT_ID: props.userPoolClient.userPoolClientId,
        ENV_NAME: props.envName,
    },
    timeout: cdk.Duration.seconds(15),
});

// API Gateway mit Custom Authorizer
const authorizer = new apigateway.TokenAuthorizer(this, 'CustomAuthorizer', {
    handler: authorizerLambda,
    identitySource: 'method.request.header.Authorization',
});
_images/API.png _images/S3.png

5.3. TypeScript CDK / Infrastructure as Code

AWS CDK mit TypeScript ermöglicht die Definition und Verwaltung von Cloud-Infrastruktur durch typsicheren Code und bietet alle Vorteile moderner Software-Entwicklung für Infrastructure as Code.

5.3.1. Stack-Architektur und Modulares Design

Eine modulare Stack-Architektur ermöglicht die unabhängige Entwicklung und Deployment verschiedener Infrastruktur-Komponenten und folgt dem Prinzip der Separation of Concerns.

Architektur-Prinzipien:

  • Base Stack: Gemeinsame Konfiguration und Tags für alle Stacks

  • Dependency Injection: Explizite Übergabe von Ressourcen zwischen Stacks

  • Environment Separation: Umgebungsspezifische Konfigurationen ohne Code-Duplikation

// Base Stack für gemeinsame Konfiguration
export interface BaseStackProps extends cdk.StackProps {
    envName: string;
    envConfig: {
        domainName: string;
        certificateArn: string;
        stage: string;
    };
}

export class BaseStack extends cdk.Stack {
    constructor(scope: Construct, id: string, props: BaseStackProps) {
        super(scope, id, props);

        // Gemeinsame Tags für alle Ressourcen
        cdk.Tags.of(this).add('Environment', props.envName);
        cdk.Tags.of(this).add('Project', 'MultiTenantSaaS');
    }
}

Stack-Hierarchie in unserem Projekt:

  1. NetworkStack: VPC, Subnets, Security Groups, NAT Gateways

  2. DatabaseStack: Aurora Cluster, Secrets Manager, Migration Lambda

  3. AuthStack: Cognito User Pools, Identity Provider Configuration

  4. ApiStack: API Gateway, Lambda Functions, Custom Authorizer

  5. EventsStack: EventBridge, SQS Queues, SNS Topics

  6. FrontendStack: S3 Bucket, CloudFront Distribution

5.3.2. Environment-spezifische Konfiguration und Deployment

Unterschiedliche Umgebungen erfordern verschiedene Ressourcen-Konfigurationen ohne Code-Änderungen. Unsere Lösung verwendet JSON-basierte Konfigurationsdateien für umgebungsspezifische Parameter.

Konfiguration-Management:

  • Development: Minimale Ressourcen für Kosten-Optimierung

  • Staging: Production-ähnliche Konfiguration für realistische Tests

  • Production: Hochverfügbare und skalierbare Ressourcen-Konfiguration

{
    "environments": {
        "dev": {
            "domainName": "dev-api.multitenant-saas.com",
            "stage": "dev",
            "database": {
                "minCapacity": 0.5,
                "maxCapacity": 2,
                "backupRetention": 7
            },
            "lambda": {
                "memorySize": 256,
                "timeout": 30
            }
        },
        "prod": {
            "domainName": "api.multitenant-saas.com",
            "stage": "prod",
            "database": {
                "minCapacity": 2,
                "maxCapacity": 16,
                "backupRetention": 30
            },
            "lambda": {
                "memorySize": 1024,
                "timeout": 60
            }
        }
    }
}

5.3.3. Custom Constructs und Wiederverwendbare Komponenten

Custom Constructs abstrahieren wiederkehrende Infrastruktur-Patterns und ermöglichen die Kapselung von Best Practices in wiederverwendbaren Komponenten.

Construct-Patterns:

  • Multi-Tenant Lambda: Lambda-Funktion mit vorkonfiguriertem Tenant-Kontext

  • Secure Database Connection: Database-Zugriff mit automatischer Secret-Rotation

  • Event-Driven Resource: EventBridge-Integration mit standardisierten Event-Schemas

// Custom Construct für Multi-Tenant Lambda Functions
export interface MultiTenantLambdaProps {
    handler: string;
    environment: Record<string, string>;
    tenantTable: dynamodb.Table;
    vpc?: ec2.Vpc;
    securityGroup?: ec2.SecurityGroup;
}

export class MultiTenantLambda extends Construct {
    public readonly function: lambda.Function;

    constructor(scope: Construct, id: string, props: MultiTenantLambdaProps) {
        super(scope, id);

        this.function = new lambda.Function(this, 'Function', {
            runtime: lambda.Runtime.NODEJS_18_X,
            handler: props.handler,
            code: lambda.Code.fromAsset(path.join(__dirname, '../dist')),
            environment: {
                ...props.environment,
                TENANT_TABLE: props.tenantTable.tableName,
            },
            timeout: cdk.Duration.seconds(30),
            vpc: props.vpc,
            securityGroups: props.securityGroup ? [props.securityGroup] : undefined,
        });

        // Automatische Berechtigungs-Vergabe
        props.tenantTable.grantReadWriteData(this.function);
    }
}

5.4. CI/CD

Automatisierte CI/CD-Pipelines gewährleisten zuverlässige und reproduzierbare Deployments der Multi-Tenant-SaaS-Anwendung mit integrierter Qualitätssicherung und Rollback-Mechanismen.

5.4.1. Deployment Pipeline und Automatisierung

Eine robuste Deployment-Pipeline automatisiert den gesamten Prozess von Code-Änderungen bis zur Production-Bereitstellung und minimiert menschliche Fehler durch konsistente Automatisierung.

Pipeline-Stages:

  1. Source Stage: Git-Repository Integration mit automatischen Triggern

  2. Build Stage: TypeScript-Kompilierung, Linting, Unit Tests

  3. Test Stage: Integration Tests, Security Scans, Infrastructure Validation

  4. Deploy Dev: Automatisches Deployment in Development-Umgebung

  5. Deploy Staging: Manual Approval für Staging-Deployment

  6. Deploy Production: Multi-Approval-Prozess für Production-Release

# PowerShell Deployment-Script für Multi-Environment-Support
param(
    [Parameter(Mandatory=$true)]
    [ValidateSet("dev", "staging", "prod")]
    [string]$Environment,

    [switch]$SkipBuild,
    [string]$Region = "us-east-1"
)

$ErrorActionPreference = "Stop"
Write-Host "Starting deployment for environment: $Environment" -ForegroundColor Green
Write-Host "Target region: $Region" -ForegroundColor Green

# AWS Environment Setup
$env:CDK_DEFAULT_REGION = $Region
$env:CDK_DEFAULT_ACCOUNT = (aws sts get-caller-identity --query Account --output text)

if (-not $env:CDK_DEFAULT_ACCOUNT) {
    Write-Error "Unable to get AWS account ID. Please ensure AWS CLI is configured."
    exit 1
}

Write-Host "Deploying to account: $env:CDK_DEFAULT_ACCOUNT" -ForegroundColor Yellow
_images/API.png

5.4.2. Infrastructure Deployment und Blue-Green Strategies

Blue-Green Deployment ermöglicht Zero-Downtime-Updates durch parallele Deployment-Umgebungen und graduellen Traffic-Shift zwischen den Versionen.

Deployment-Strategien:

  • Blue-Green Deployment: Parallele Stacks für risikofreie Updates

  • Traffic Shifting: Graduelle Umleitung über API Gateway Weighted Routing

  • Automated Rollback: Automatisches Zurückschalten bei Anomalien oder Fehlern

// Migration Lambda für Database Schema Updates
this.migrationLambda = new lambda.Function(this, 'MigrationLambda', {
    runtime: lambda.Runtime.NODEJS_18_X,
    handler: 'migration.handler',
    code: lambda.Code.fromAsset(path.join(__dirname, '../../../backend/dist/lambdas/database')),
    environment: {
        DB_CLUSTER_ARN: this.database.clusterArn,
        DB_SECRET_ARN: this.dbCredentials.secretArn,
        DB_NAME: 'tenantdb',
        ENV_NAME: props.envName,
    },
    timeout: cdk.Duration.minutes(10),
    vpc: props.vpc,
    vpcSubnets: {
        subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
    },
    securityGroups: [lambdaSecurityGroup],
});

Database Migration Patterns:

  • Forward-Compatible Changes: Schema-Änderungen, die mit alter Code-Version kompatibel sind

  • Rollback Scripts: Automatische Rollback-Mechanismen für kritische Schema-Änderungen

  • Version Management: Schema-Versioning für nachvollziehbare Database-Evolution

5.4.3. Monitoring und Alerting Integration

Integriertes Monitoring und Alerting ermöglichen proaktive Überwachung der Anwendungsgesundheit und schnelle Reaktion auf Probleme in Production.

Monitoring-Components:

  • CloudWatch Metrics: Custom Metrics für Business Logic und Performance

  • Structured Logging: JSON-basierte Logs für automatisierte Analyse

  • Alert Management: SNS-Topics für verschiedene Kritikalitätsstufen

// CloudWatch Alarm für kritische API-Fehlerrate
const errorAlarm = new cloudwatch.Alarm(this, 'HighErrorRate', {
    metric: this.api.metricClientError(),
    threshold: 10,
    evaluationPeriods: 2,
    treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
});

// SNS Topic für Alert-Benachrichtigungen
const alertTopic = new sns.Topic(this, 'AlertTopic', {
    displayName: 'SaaS Platform Alerts',
    fifo: false,
});

// Alarm Action für automatische Benachrichtigung
errorAlarm.addAlarmAction(new snsActions.SnsAction(alertTopic));

5.5. Backend

Das Backend implementiert die gesamte Multi-Tenant-Business-Logic in einer serverlosen, event-driven Architektur und gewährleistet optimale Skalierbarkeit und Tenant-Isolation.

5.5.1. Lambda Functions und Service-Architektur

Mikroservice-orientierte Lambda-Architektur ermöglicht die unabhängige Entwicklung und Skalierung verschiedener Business-Domains mit klaren Verantwortlichkeiten.

_images/mirco.png

Service-Domains:

  • Tenant Management: CRUD-Operationen für Tenant-Verwaltung und Onboarding

  • User Management: Benutzer-Lifecycle innerhalb von Tenant-Kontexten

  • Resource Management: Business-Ressourcen-Verwaltung mit Tenant-Isolation

  • Event Processing: Asynchrone Event-Verarbeitung für Cross-Service-Kommunikation

// Tenant Manager Lambda - Haupteinstiegspunkt für Tenant-Operationen
export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
    const clusterArn = process.env.DB_CLUSTER_ARN as string;
    const secretArn = process.env.DB_SECRET_ARN as string;
    const database = process.env.DB_NAME as string;
    const eventBusName = process.env.EVENT_BUS_NAME as string;

    try {
        // Master-Schema für Tenant-Metadaten initialisieren
        await executeQuery({
            clusterArn,
            secretArn,
            database,
            sql: `
                CREATE TABLE IF NOT EXISTS master.tenants (
                    id VARCHAR(36) PRIMARY KEY,
                    name VARCHAR(255) NOT NULL,
                    tier VARCHAR(50) NOT NULL,
                    status VARCHAR(50) NOT NULL,
                    contact_email VARCHAR(255) NOT NULL,
                    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
                );
            `,
        });

        // Request-Routing basierend auf HTTP-Method
        switch (event.httpMethod) {
            case 'GET':
                return await handleGetTenants(event, clusterArn, secretArn, database);
            case 'POST':
                return await handleCreateTenant(event, clusterArn, secretArn, database, eventBusName);
            case 'PUT':
                return await handleUpdateTenant(event, clusterArn, secretArn, database, eventBusName);
            case 'DELETE':
                return await handleDeleteTenant(event, clusterArn, secretArn, database, eventBusName);
            default:
                return formatResponse(405, { message: 'Method not allowed' });
        }
    } catch (error) {
        console.error('Error in tenant manager:', error);
        return formatResponse(500, { message: 'Internal server error' });
    }
};
_images/tenant.png

5.5.2. Multi-Tenant Security und Authorization

Tenant-aware Security gewährleistet, dass jeder Request nur auf autorisierte Tenant-Daten zugreifen kann und implementiert mehrschichtige Sicherheitsmechanismen.

Security-Layers:

  • JWT Token Validation: Cognito-basierte Token-Verifizierung mit Custom Claims

  • Tenant Context Injection: Automatische Extraktion und Validierung des Tenant-Kontexts

  • Database-Level Isolation: Schema-basierte Trennung auf Datenbankebene

// Custom Authorizer für JWT Token Validation mit Tenant-Kontext
export const handler = async (event: APIGatewayTokenAuthorizerEvent): Promise<APIGatewayAuthorizerResult> => {
    try {
        // Token aus Authorization Header extrahieren
        const token = event.authorizationToken.replace('Bearer ', '');

        // Environment-spezifische Konfiguration
        const userPoolId = process.env.USER_POOL_ID as string;
        const appClientId = process.env.APP_CLIENT_ID as string;
        const region = process.env.AWS_REGION as string;

        // JWKs Download und Caching für Performance
        if (!jwksCache) {
            const jwksUrl = `https://cognito-idp.${region}.amazonaws.com/${userPoolId}/.well-known/jwks.json`;
            const response = await fetch(jwksUrl);
            jwksCache = await response.json();

            // JWKs zu PEMs konvertieren für JWT-Verifizierung
            jwksCache.keys.forEach((key: any) => {
                const keyId = key.kid;
                const modulus = key.n;
                const exponent = key.e;
                const jwk = { kty: key.kty, n: modulus, e: exponent };
                const pem = jwkToPem(jwk);
                pems[keyId] = pem;
            });
        }

        // Token dekodieren und verifizieren
        const decodedJwt: any = jwt.decode(token, { complete: true });
        const kid = decodedJwt.header.kid;
        const pem = pems[kid];

        if (!pem) {
            throw new Error('Invalid token signing key');
        }

        // JWT-Signature verifizieren und Claims extrahieren
        const verified: any = jwt.verify(token, pem, { algorithms: ['RS256'] });
        const tenantId = verified['custom:tenantId'];

        // Policy für API Gateway mit Tenant-Kontext generieren
        return generatePolicy(verified.sub, 'Allow', event.methodArn, {
            tenantId: tenantId,
            userId: verified.sub,
            userRole: verified['custom:role']
        });

    } catch (error) {
        console.error('Authorization failed:', error);
        return generatePolicy('user', 'Deny', event.methodArn);
    }
};

5.5.3. Event-Driven Architecture und Asynchrone Verarbeitung

Event-driven Architektur ermöglicht lose Kopplung zwischen Services und skalierbare Verarbeitung von Tenant-Lifecycle-Events.

Event-Processing Patterns:

  • Tenant Created: Schema-Erstellung, Default-Ressourcen, Welcome-Email

  • Tenant Updated: Quota-Updates, Tier-Changes, Permission-Adjustments

  • Tenant Deleted: Cleanup-Prozesse, Daten-Archivierung, Resource-Freigabe

// Event Handler für Tenant-Lifecycle-Events
export const handler = async (
    event: EventBridgeEvent<string, any>,
    context: Context
): Promise<void> => {
    try {
        // Tenant-Kontext aus Event extrahieren
        const tenantId = getEventTenantContext(event);
        const { detail, 'detail-type': detailType } = event;

        // Event-Type-basierte Verarbeitung
        switch (detailType) {
            case EventTypes.TENANT_CREATED:
                await handleTenantCreated(detail, tenantId);
                break;

            case EventTypes.TENANT_UPDATED:
                await handleTenantUpdated(detail, tenantId);
                break;

            case EventTypes.TENANT_DELETED:
                await handleTenantDeleted(detail, tenantId);
                break;

            default:
                console.log(`Unhandled event type: ${detailType}`);
        }
    } catch (error) {
        console.error('Error processing tenant event:', error);
        throw error;
    }
};

// Tenant-Schema-Initialisierung für neue Tenants
async function createTenantSchema(tenantId: string): Promise<void> {
    const schemaName = `tenant_${tenantId}`;

    // Tenant-spezifisches Schema erstellen
    await rdsClient.send(new ExecuteStatementCommand({
        resourceArn: clusterArn,
        secretArn: secretArn,
        database: database,
        sql: `CREATE SCHEMA IF NOT EXISTS ${schemaName}`
    }));

    // Tenant-spezifische Tabellen-Struktur
    await rdsClient.send(new ExecuteStatementCommand({
        resourceArn: clusterArn,
        secretArn: secretArn,
        database: database,
        sql: `
            CREATE TABLE IF NOT EXISTS ${schemaName}.resources (
                id VARCHAR(36) PRIMARY KEY,
                name VARCHAR(255) NOT NULL,
                description TEXT,
                type VARCHAR(50) NOT NULL,
                data JSON,
                ownerId VARCHAR(36) NOT NULL,
                createdAt TIMESTAMP NOT NULL,
                updatedAt TIMESTAMP NOT NULL
            )
        `
    }));

    console.log(`Created database schema for tenant ${tenantId}`);
}

5.6. Fazit

Die Entwicklung einer cloudbasierten Multi-Tenant-SaaS-Plattform mit AWS demonstriert die Stärken der Amazon Web Services Infrastruktur für moderne Anwendungsarchitekturen. Die serverless-first Architektur mit AWS Lambda, API Gateway und Aurora Serverless v2 ermöglicht automatische Skalierung und kosteneffiziente Pay-per-Use-Ressourcennutzung. AWS Cognito bietet robuste Authentifizierung mit Multi-Tenant-fähigen Custom Claims, während EventBridge eine lose gekoppelte, event-driven Architektur ermöglicht. TypeScript CDK als Infrastructure as Code Lösung reduziert Konfigurationsfehler durch typsichere Definitionen und ermöglicht wiederverwendbare Konstrukte. Die umfassenden AWS-Sicherheitsfeatures von VPC-Isolation bis IAM-Integration gewährleisten Defense-in-Depth für kritische Multi-Tenant-Datenisolation. Herausforderungen wie Architektur-Komplexität, Lambda Cold Starts und Kostenmanagement bei hohem Traffic erfordern kontinuierliches Monitoring und Optimierung. AWS bietet insgesamt eine ausgereifte Plattform für skalierbare Multi-Tenant-SaaS-Lösungen, wobei der Erfolg von sorgfältiger Architekturplanung und datenbasierter Optimierung abhängt.

5.7. Quellen