Alphazed betreibt sein gesamtes Backend — und bedient damit über 95.000 Schüler in mehr als 50 Ländern — mit AWS Lambda und dem Serverless Framework. Die Architektur nutzt Flask auf Lambda hinter API Gateway, MySQL 8 auf RDS, S3 für die Inhaltsbereitstellung und einen maßgeschneiderten Analytik-See (SQS → Kinesis Firehose → S3 → Glue → Athena). Dünne Lambda-Handler optimieren die Kaltstartlatenz, und das System bedient über 7 Apps von einer einzigen Codebasis mit Laufzeiteinstellungsänderungen.
Warum Serverless für EdTech?
Bildungs-Apps haben unvorhersehbare Nutzungsmuster:
- Werktagmorgen: Eltern laden App vor dem Schulbesuch ihres Kindes herunter (Traffic-Spitze)
- Nachmittags: Nachschulische Übungssitzungen (anhaltende Last)
- Wochenenden: Intensive Marathonsitzungen (2-3x normale Last)
- Während Ramadan: Nutzung am Abend explodiert (Familien-Koran-Sitzungen)
- Schulferien: Gänzlich anderes Muster
Vorteile von Serverless:
- Pay-per-Request-Preismodell: Man zahlt nur für tatsächliche Nutzung. Bei 10 Nutzern, die die API aufrufen, zahlt man 10 Aufrufe. Wenn 100.000 in einem viralen Moment zugreifen, skaliert alles sofort.
- Keine Kaltstarts für hochfrequentierte Endpunkte: Wir verwenden "immer warme" Lambda-Layer für häufig aufgerufene Endpunkte
- Auto-Scaling: 10 gleichzeitige Nutzer oder 10.000 bewältigen ohne Infrastrukturänderungen
- Keine Serverwartung: Das Team konzentriert sich auf Curriculum und KI, nicht auf Kubernetes-Cluster oder Load Balancer
Architektur Deep-Dive
API Gateway → Lambda → RDS
[Client-App] (iOS, Android, Web)
↓
[API Gateway] (HTTP-Routing, Ratenbegrenzung)
↓
[Lambda-Handler] (Flask-App, 512MB Speicher, 28s Timeout)
├── App-Routen: /app/* (mobile Endpunkte)
├── Nutzer-Routen: /user/* (autorisierte Endpunkte)
└── Admin-Routen: /boss/* (Admin-Dashboard)
↓
[MySQL 8 auf RDS] (Persistente Daten)
↓
[Antwort] (JSON zurück an den Client)
Dünne Lambdas für Geschwindigkeit
Die meisten Lambdas sind bewusst minimal:
# Dünner Handler (~100KB)
import json
import pymysql
def get_user_progress(event, context):
user_id = event['pathParameters']['user_id']
# Direkte DB-Verbindung (kein ORM-Overhead)
conn = pymysql.connect(host='rds.aws.com', user='app', password='...', database='amal')
cursor = conn.cursor()
cursor.execute(
'SELECT concept_id, accuracy FROM user_memory WHERE user_id = %s',
(user_id,)
)
rows = cursor.fetchall()
conn.close()
return {
'statusCode': 200,
'body': json.dumps([{'concept': r[0], 'accuracy': r[1]} for r in rows])
}
Kein Flask-Import, kein SQLAlchemy ORM, keine Middleware. Ergebnis: ~500ms Kaltstart vs. 5-10s für vollständige Flask-App.
Schwere Endpunkte (Inhaltsgenerierung, Analytik-Verarbeitung) nutzen vollständiges Flask:
# Schwerer Handler (~30MB mit Flask, SQLAlchemy, numpy)
from flask import Flask, jsonify
from models import UserMemory
import numpy as np
app = Flask(__name__)
@app.route('/content_duo/generate', methods=['POST'])
def generate_content_duo():
# Komplexe Logik, die ORM erfordert
user = UserMemory.query.filter_by(user_id=request.json['user_id']).first()
# ... personalisierte Sitzung erzeugen ...
return jsonify(session_data)
Trade-off: Kaltstarts sind langsamer, diese werden jedoch seltener aufgerufen.
Pro-App-Tabellenpräfixierung
Eine RDS-Instanz bedient über 7 Apps mit datenbankseitiger Isolation:
-- Amal-App
CREATE TABLE amal_users (...)
CREATE TABLE amal_content_bytes (...)
CREATE TABLE amal_user_memory (...)
-- Thurayya-App
CREATE TABLE thurayya_users (...)
CREATE TABLE thurayya_content_bytes (...)
CREATE TABLE thurayya_user_memory (...)
-- Andere Apps: qais_*, kiDelite_*, etc.
Zum Bereitstellungszeitpunkt wählt die APP_NAME-Umgebungsvariable das Präfix aus:
app_name = os.getenv('APP_NAME', 'amal') # 'amal', 'thurayya', 'qais', etc.
# Abfragen verwenden dynamisch Präfix
table_name = f'{app_name}_users'
cursor.execute(f'SELECT * FROM {table_name} WHERE id = %s', (user_id,))
Der Analytics-See
Problem: Direkte Datenbankabfragen für Analytik verlangsamen die Produktion. Berichte blockieren Tabellen.
Lösung: Asynchrone Analytik-Pipeline
[Mobile App]
↓ (senden Ereignis)
[API-Endpunkt] → [SQS-Queue] (asynchron)
↓ (antwortet sofort auf App)
↓ (wartet nicht auf Analytik)
[Kinesis Firehose] (bündelt Ereignisse alle 5 Minuten oder wenn 100 MB erreicht)
↓
[S3] (partitioniert: s3://analytics-lake/amal/2026/03/28/events.parquet)
↓
[AWS Glue] (durchsucht S3, ermittelt Schema)
↓
[Athena] (SQL-Abfragen über Presto-Engine)
↓
[Dashboard] (zeigt Echtzeiteinblicke)
Dead Letter Queue (DLQ) Muster
Wenn die Analytik fehlschlägt:
SQS → [Firehose schlägt fehl]
↓
[DLQ empfängt fehlgeschlagene Nachrichten]
↓
[Alarm an Betrieb gesendet]
↓
[Produktions-API ist nicht betroffen]
Analytik blockiert niemals Benutzeranforderungen. Kinder können lernen, selbst wenn die Analytik-Pipeline ausfällt.
Kostenoptimierungsstrategien
Strategie 1: Dünne Lambdas für hochfrequentierte Endpunkte
- Typische mobile App führt 10-20 API-Aufrufe pro Sitzung durch
- 95.000 aktive Nutzer × 3 Sitzungen/Tag × 15 Aufrufe/Sitzung = 4,275M Aufrufe/Tag
- Wenn jeder Aufruf $0.0000002 kostet (Lambda-Preisgestaltung), sind das $0.86/Tag
- Reduzierung der Kaltstartzeit um 10s spart ~500 USD/Monat
Strategie 2: RDS Reservierte Instanzen
- 3-jährige Reservierungsverpflichtung: ~60% Rabatt im Vergleich zu bedarfsgerecht
- Wir verwenden `db.r6i.xlarge` (4 vCPU, 32GB RAM): $2,800/Monat reserviert vs. $6,500/Monat bedarfsgerecht
- Jahresersparnis: ~50.000 USD
Strategie 3: Caching
- Häufig aufgerufene Daten (Curriculum, Inhaltsbytes) im ElastiCache (Redis) zwischengespeichert
- Reduziert RDS-Abfragen um 70%
- Kosten: $800/Monat für Cache, spart $2.000/Monat in RDS
Betreiben von 7+ Apps aus einer Codebasis
| App | Präfix | DB-Tabellen | Lambda-Stack | Status |
|---|---|---|---|---|
| Amal | `amal_` | 40+ Tabellen | Gemeinsam genutzt | Produktion |
| Thurayya | `thurayya_` | 40+ Tabellen | Gemeinsam genutzt | Produktion |
| Qais | `qais_` | 35+ Tabellen | Gemeinsam genutzt | Beta |
| KidElite | `kidelite_` | 40+ Tabellen | Gemeinsam genutzt | Produktion |
| Alphazed School | `school_` | 50+ Tabellen | Gemeinsam genutzt | Beta |
| Alphazed Montessori | `montessori_` | 45+ Tabellen | Gemeinsam genutzt | Intern |
Ein Backend, eine Bereitstellungspipeline, 6 gleichzeitige Apps. Neue App-Entwicklung: Wochen statt Monate.
FAQ
F: Hat Lambda nicht ein 15-Minuten-Timeout-Limit? A: Lambda hat ein 15-Minuten-Maximum-Timeout, aber wir benötigen selten langlaufende Anforderungen. Schwerwiegende Workloads (Inhaltsgenerierung, große Exporte) nutzen asynchrone Jobs mit SQS + Step Functions.
F: Was, wenn die Datenbank ausfällt? A: RDS hat Multi-AZ-Failover (primär + Standby-Replikat). Das Failover erfolgt automatisch und dauert ~60 Sekunden. Die Clients erleben kurze Zeitüberschreitungen, aber die Wiederherstellung erfolgt schnell.
F: Wie handhabt ihr die Datenbankverbindungs-Pooling mit zustandslosen Lambda? A: Jede Lambda-Instanz behält einen Verbindungs-Pool bei (wird über warme Aufrufe wiederverwendet). Kaltstarts erhalten neue Verbindungen. RDS Proxy sitzt zwischen Lambda und RDS, um Verbindungsgrenzen zu verwalten.



