Wie wir eine Multi-App-Plattform aus einer Codebasis erstellt haben
Alphazed betreibt über 7 Lernapps (Amal, Thurayya, Qais, KidElite, Alphazed School, Alphazed Montessori und mehr) aus einer einzigen Backend-Codebasis und einem gemeinsamen Flutter-Mobile-Framework. Jede App erhält ihre eigenen Datenbanktabellen (mit Präfix), Konfiguration, Push-Benachrichtigungen, E-Mail-Vorlagen und Inhalte — teilt jedoch die Authentifizierung (AWS Cognito), die Analyseinfrastruktur und die Kernlernalgorithmen.
Backend: App-Auswahl zur Laufzeit
Funktionsweise
Bei der Bereitstellung wählt eine Umgebungsvariable die App aus:
# Bereitstellung Amal
export APP_NAME=amal
serverless deploy
# Bereitstellung Thurayya
export APP_NAME=thurayya
serverless deploy
# Bereitstellung Qais
export APP_NAME=qais
serverless deploy
Jede Bereitstellung erstellt unabhängige Lambda-Funktionen, API Gateway-Routen und Überwachungen, aber sie verbinden sich alle mit derselben Backend-Codebasis.
Drei-Stufen-Konfigurationspriorität
# src/config.py
import os
app_name = os.getenv('APP_NAME', 'amal')
# Stufe 1: Exakte App-Übereinstimmung
config = load_json(f'config/{app_name}.json')
# Stufe 2: App-Familien-Übereinstimmung (wenn keine exakte Konfiguration vorhanden ist)
if not config:
family = app_name.split('_')[0] # 'amal_beta' → 'amal'
config = load_json(f'config/{family}.json')
# Stufe 3: Standard
if not config:
config = load_json('config/default.json')
App-spezifische Konfiguration (config/amal.json)
{
"app_name": "amal",
"app_id": "com.alphazed.amal",
"database": {
"table_prefix": "amal_"
},
"email": {
"template_dir": "templates/amal",
"from_address": "amal@alphazed.com",
"from_name": "Amal Team"
},
"push_notifications": {
"firebase_project": "alphazed-amal",
"apns_certificate": "certs/amal.pem"
},
"content": {
"s3_bucket": "amal-content-production",
"curriculum_id": "amal_v3_arabic"
},
"feature_flags": {
"enable_tajweed_feedback": false,
"enable_noorani_qaida": false,
"enable_juz_amma": false,
"enable_creature_building": true,
"enable_physics_games": true
},
"pricing": {
"monthly_usd": 6.99,
"yearly_usd": 67.99,
"trial_days": 14
}
}
Geteilte vs. App-spezifische Ressourcen
| Ressource | Geteilt | App-spezifisch | Grund |
|---|---|---|---|
| Lambda-Code | ✓ Ja | ✗ Nein | Eine Codebasis, Zweige durch APP_NAME |
| RDS-Instanz | ✓ Ja | ✗ Nein (Tabellen mit Präfix) | Kostensenkung, ein Backup |
| S3-Inhalte | ✗ Nein | ✓ Ja | Jede App hat einen eigenen Lehrplan |
| AWS Cognito | ✓ Ja | ✗ Nein (über app_id) | Zentrale Authentifizierung, app_id differenziert |
| Analytics-See | ✓ Ja | ✗ Nein (partitioniert nach App) | Einheitliche Pipeline, getrennte Daten |
| Firebase-Messaging | ✗ Nein | ✓ Ja | App-spezifische APNs + FCM |
Frontend: Flutter Multi-App-Architektur
Geteilter Kern (packages/alphazed_common)
- Zustandsverwaltung (Riverpod)
- Audioverarbeitung
- Animationsbibliothek (Rive-Integration)
- Designsyst...client
App-spezifische Schichten (apps/amal, apps/thurayya, etc.)
apps/
├── amal/
│ ├── lib/
│ │ ├── main.dart (App-Einstiegspunkt)
│ │ ├── config/
│ │ │ ├── curriculum.json (Amals Lektionenstruktur)
│ │ │ ├── colors.dart (Amals Thema)
│ │ │ └── characters.json (Avatar-Anpassung)
│ │ └── screens/ (Amal-spezifische Bildschirme)
│ └── pubspec.yaml (Amal-Abhängigkeiten)
│
├── thurayya/
│ ├── lib/
│ │ ├── main.dart (anderer Einstiegspunkt)
│ │ ├── config/
│ │ │ ├── curriculum.json (Juz Amma Struktur)
│ │ │ ├── colors.dart (Thurayyas Thema)
│ │ │ └── characters.json (Avatar-Anpassung)
│ │ └── screens/ (Thurayya-spezifische Bildschirme)
│ └── pubspec.yaml (Thurayya-Abhängigkeiten)
│
└── packages/
└── alphazed_common/ (geteilter Code)
Build-Zeit-Konfiguration
# Erstelle Amal
flutter build apk --dart-define=APP_NAME=amal --dart-define=CURRICULUM=amal_v3
# Erstelle Thurayya
flutter build apk --dart-define=APP_NAME=thurayya --dart-define=CURRICULUM=thurayya_juzamma
Zur Build-Zeit erhält jede App ihre eigenen Lehrplandaten, Farben und die Konfiguration. Einzelnes Code-Repository, mehrere kompilierte Binärdateien.
Bereitstellung: Unabhängige Stacks
Serverless-Framework-Konfiguration
# serverless.yml
service: alphazed-backend
custom:
pythonRequirements:
dockerizePip: true
app_name: ${env:APP_NAME}
functions:
# Diese Funktionen werden für jeden APP_NAME-Wert bereitgestellt
get_user:
handler: src/handlers/user.get_user
events:
- http:
path: app/{app_name}/user/{user_id}
method: get
content_duo:
handler: src/handlers/content.generate_content_duo
timeout: 20
events:
- http:
path: app/{app_name}/content_duo
method: post
resources:
Resources:
# Jede App erhält ihre eigene CloudWatch-Protokollgruppe
${self:custom.app_name}LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/alphazed-${self:custom.app_name}
RetentionInDays: 30
Wenn Sie mit APP_NAME=amal bereitstellen, erstellt CloudFormation:
- Lambda-Funktionen mit Präfix
alphazed-amal-* - API Gateway-Routen unter
/amal/* - CloudWatch-Protokolle unter
/aws/lambda/alphazed-amal - Unabhängige Skalierung und Überwachung
Warum das wichtig ist
Für Produktteams
- Neue App-Einführung: Wochen statt Monate (gemeinsame Infrastruktur)
- Funktionsparität: Fehlerbehebungen und Algorithmusverbesserungen gelten sofort für alle Apps
- A/B-Tests: Eigenschaften können einfach in einer App getestet werden, bevor sie auf andere ausgeweitet werden
Für die Technik
- Einzige Codebasis: ein Testset, eine CI/CD-Pipeline
- Geteilte Lernalgorithmen: Alle Apps profitieren von Abstandsrepetition und Inhaltemischung
- Betriebseffizienz: Ein Team verwaltet die gesamte Infrastruktur
Für die Kosten
- Gemeinsames RDS: Bruchteil der Kosten von 7 unabhängigen Datenbanken
- Gemeinsames Cognito: ein Authentifizierungsanbieter für alle Apps
- Gemeinsame Analyse: eine Pipeline, die 7 Apps bedient
- Geschätzte Kosteneinsparungen: $40.000-60.000/Jahr im Vergleich zu separaten Backends
Herausforderungen und Lösungen
Herausforderung 1: Konflikte im Datenbankschema
- Amal benötigt
amal_user_memory-Tabelle - Thurayya benötigt
thurayya_user_memory-Tabelle (unterschiedliche Felder für Koran-Memorisation) - Lösung: Migrationen sind App-spezifisch.
migrations/001_amal_user_memory.sqlwerden nur ausgeführt, wennAPP_NAME=amal
Herausforderung 2: Unterschiedliche Funktionssätze
- Thurayya hat Tajweed-Feedback; Amal nicht
- Amal hat Physikspiele; Thurayya nicht
- Lösung: Funktionsflags in der Konfiguration (
enable_tajweed_feedback,enable_physics_games)
Herausforderung 3: Unabhängige Skalierung
- Amal sieht Traffic-Spitze; Thurayya ist ruhig
- Gemeinsamer Lambda-Pool bedeutet, dass sie um Gleichzeitigkeit konkurrieren
- Lösung: CloudWatch-Skalierungsrichtlinien pro App. Wenn
amal-*Lambdas das Gleichzeitigkeitlimit überschreiten, automatisch unabhängig skalieren
FAQ
F: Führt die gemeinsame Nutzung einer einzigen Codebasis nicht zur Koppelung zwischen Apps?
A: Nein. Der Code jeder App befindet sich in separaten Verzeichnissen (apps/amal, apps/thurayya). Gemeinsamer Code befindet sich in packages/alphazed_common und ist explizit versioniert. Eine enge Kopplung ist ein Designgeruch — wir fangen ihn bei der Codeüberprüfung ab.
F: Was passiert, wenn eine App eine breaking API-Änderung benötigt?
A: Wir versionieren APIs pro App: /amal/v1/*, /thurayya/v1/*. Apps können unabhängig aufrüsten. Alte Versionen laufen 12 Monate lang, was den App-Teams Zeit zum Aktualisieren gibt.
F: Können Apps Benutzer teilen? A: Nicht standardmäßig. Jede App hat ihre eigene Benutzertabelle (mit Präfix). Wenn ein Elternteil sowohl Amal als auch Thurayya abonnieren möchte, erstellt er separate Konten (oder wir könnten später eine „Familien“-Verknüpfungsfunktion hinzufügen).



