Alphazed gère tout son backend — desservant plus de 95,000 élèves dans plus de 50 pays — sur AWS Lambda avec le Framework Serverless. L'architecture utilise Flask sur Lambda derrière API Gateway, MySQL 8 sur RDS, S3 pour la distribution de contenu, et un lac analytique personnalisé (SQS → Kinesis Firehose → S3 → Glue → Athena). Les gestionnaires Lambda fins optimisent la latence de démarrage à froid, et le système dessert plus de 7 applications à partir d'une même base de code avec un changement de configuration au moment de l'exécution.
Pourquoi le serverless pour l'EdTech ?
Les applications éducatives ont des schémas d'utilisation imprévisibles :
- Matins de semaine : les parents téléchargent l'application avant d'envoyer l'enfant à l'école (pic de trafic)
- Après-midi de semaine : sessions de pratique après l'école (charge continue)
- Weekends : sessions marathon intensives (charge 2-3x normale)
- Pendant le Ramadan : l'utilisation en soirée explose (sessions familiales de lecture du Coran)
- Vacances scolaires : schéma complètement différent
Avantages du serverless :
- Paiement à la requête : Vous ne payez que pour l'utilisation réelle. Si 10 utilisateurs accèdent à l'API, vous payez pour 10 invocations. Si 100,000 utilisateurs y accèdent durant un moment viral, vous montez instantanément en échelle.
- Pas de démarrages à froid pour les points de terminaison haute fréquence : Nous utilisons des couches Lambda "toujours chaudes" pour les points de terminaison fréquemment appelés.
- Mise à l'échelle automatique : Gérez 10 utilisateurs simultanés ou 10,000 sans changement d'infrastructure.
- Pas de maintenance serveur : L'équipe se concentre sur le programme et l'IA, pas sur les clusters Kubernetes ou les équilibrages de charge.
Analyse approfondie de l'architecture
API Gateway → Lambda → RDS
[Application Client] (iOS, Android, Web)
↓
[API Gateway] (routage HTTP, limitation du débit)
↓
[Gestionnaires Lambda] (application Flask, 512MB de mémoire, délai d'expiration de 28s)
├── Routes d'application : /app/* (points de terminaison mobiles)
├── Routes d'utilisateur : /user/* (points de terminaison authentifiés)
└── Routes d'administration : /boss/* (tableau de bord administrateur)
↓
[MySQL 8 sur RDS] (Données persistantes)
↓
[Réponse] (JSON renvoyé au client)
Lambdas fines pour la vitesse
La plupart des Lambdas sont intentionnellement minimales :
# Gestionnaire fin (~100KB)
import json
import pymysql
def get_user_progress(event, context):
user_id = event['pathParameters']['user_id']
# Connexion directe à la DB (pas de surcharge ORM)
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])
}
Pas d'importation de Flask, pas d'ORM SQLAlchemy, pas de middleware. Résultat : démarrage à froid en ~500ms contre 5-10s pour l'application Flask complète.
Les points de terminaison lourds (génération de contenu, traitement analytique) utilisent Flask complet :
# Gestionnaire lourd (~30MB avec 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():
# Logique complexe nécessitant l'ORM
user = UserMemory.query.filter_by(user_id=request.json['user_id']).first()
# ... générer session personnalisée ...
return jsonify(session_data)
Compromis : les démarrages à froid sont plus lents, mais ceux-ci sont appelés moins fréquemment.
Préfixation par application pour les tables
Un exemplaire RDS dessert 7+ applications avec l'isolation au niveau des bases de données :
-- Application Amal
CREATE TABLE amal_users (...)
CREATE TABLE amal_content_bytes (...)
CREATE TABLE amal_user_memory (...)
-- Application Thurayya
CREATE TABLE thurayya_users (...)
CREATE TABLE thurayya_content_bytes (...)
CREATE TABLE thurayya_user_memory (...)
-- Autres applications : qais_*, kiDelite_*, etc.
Au moment du déploiement, la variable d'environnement APP_NAME sélectionne le préfixe :
app_name = os.getenv('APP_NAME', 'amal') # 'amal', 'thurayya', 'qais', etc.
# Les requêtes utilisent dynamiquement le préfixe
nom_table = f'{app_name}_users'
cursor.execute(f'SELECT * FROM {nom_table} WHERE id = %s', (user_id,))
Le lac analytique
Problème : Les requêtes directes à la base de données pour les analyses ralentissent la production. L'exécution de rapports verrouille les tables.
Solution : Pipeline analytique asynchrone
[Application mobile]
↓ (envoie l'événement)
[Point de terminaison API] → [File d'attente SQS] (asynchrone)
↓ (répond immédiatement à l'application)
↓ (n'attend pas les analyses)
[Kinesis Firehose] (regroupe les événements toutes les 5 min ou lors de l'atteinte de 100MB)
↓
[S3] (partitionné : s3://analytics-lake/amal/2026/03/28/events.parquet)
↓
[AWS Glue] (récupère les données de S3, déduit le schéma)
↓
[Athena] (requêtes SQL via le moteur Presto)
↓
[Tableau de bord] (affiche les insights en temps réel)
Modèle de File d'attente de lettres mortes (DLQ)
Si les analyses échouent :
SQS → [Échec de Firehose]
↓
[DLQ reçoit les messages échoués]
↓
[Alerte envoyée aux opérations]
↓
[L'API de production n'est pas affectée]
Les analyses ne bloquent jamais les requêtes des utilisateurs. Les enfants peuvent apprendre même si le pipeline analytique est en panne.
Stratégies d'optimisation des coûts
Stratégie 1 : Lambdas fines pour les points de terminaison haute fréquence
- L'application mobile typique effectue 10-20 appels d'API par session
- 95,000 utilisateurs actifs × 3 sessions/jour × 15 appels/session = 4,275M appels/jour
- Si chaque appel coûte $0,0000002 (tarification Lambda), cela représente $0,86/jour
- Réduire le temps de démarrage à froid de 10s économise ~500$/mois
Stratégie 2 : Instances réservées RDS
- Engagement de réservation sur 3 ans : ~60% de réduction par rapport à la demande
- Nous utilisons `db.r6i.xlarge` (4 vCPU, 32GB RAM): $2,800/mois réservés contre $6,500/mois à la demande
- Économies annuelles : ~50,000$
Stratégie 3 : Mise en cache
- Données fréquemment consultées (programme, octets de contenu) mises en cache dans ElastiCache (Redis)
- Réduit les requêtes RDS de 70%
- Coût : 800$/mois pour le cache, économise 2,000$/mois en RDS
Soutenir plus de 7 applications à partir d'une base de code unique
| Application | Préfixe | Tables DB | Pile Lambda | Statut |
|---|---|---|---|---|
| Amal | `amal_` | 40+ tables | Partagé | Production |
| Thurayya | `thurayya_` | 40+ tables | Partagé | Production |
| Qais | `qais_` | 35+ tables | Partagé | Bêta |
| KidElite | `kidelite_` | 40+ tables | Partagé | Production |
| Alphazed School | `school_` | 50+ tables | Partagé | Bêta |
| Alphazed Montessori | `montessori_` | 45+ tables | Partagé | Interne |
Un backend, un pipeline de déploiement, 6 applications simultanées. Lancement d'une nouvelle application : semaines au lieu de mois.
FAQ
Q : Lambda n'a-t-il pas une limite de délai d'attente de 15 minutes ? R : Lambda a un délai d'attente maximum de 15 minutes, mais nous avons rarement besoin de requêtes de longue durée. Les charges lourdes (génération de contenu, exportations volumineuses) utilisent des tâches asynchrones avec SQS + Step Functions.
Q : Que se passe-t-il si la base de données tombe en panne ? R : RDS a un basculement Multi-AZ (réplica principal + de secours). Le basculement est automatique et prend ~60 secondes. Les clients voient de brèves expirations, mais la récupération est rapide.
Q : Comment gérez-vous le regroupement des connexions à la base de données avec Lambda sans état ? R : Chaque instance Lambda maintient un pool de connexions (réutilisé à travers les invocations chaudes). Les démarrages à froid obtiennent de nouvelles connexions. RDS Proxy se place entre Lambda et RDS pour gérer les limites de connexion.



