Come Abbiamo Costruito una Piattaforma Multi-App da un Singolo Codice
Alphazed gestisce più di 7 app educative (Amal, Thurayya, Qais, KidElite, Alphazed School, Alphazed Montessori e altre) partendo da un singolo codice backend e da un framework mobile Flutter condiviso. Ogni app dispone delle proprie tabelle nel database (con prefisso), configurazioni, notifiche push, modelli email e contenuti, ma condivide l'autenticazione (AWS Cognito), l'infrastruttura analitica e gli algoritmi di apprendimento di base.
Backend: Selezione dell'App a Runtime
Come Funziona
Al momento del deploy, una variabile d'ambiente seleziona l'app:
# Deploy Amal
export APP_NAME=amal
serverless deploy
Deploy Thurayya
export APP_NAME=thurayya
serverless deploy
Deploy Qais
export APP_NAME=qais
serverless deploy
Ogni deployment crea funzioni Lambda indipendenti, rotte API Gateway e monitoraggio, ma tutte si connettono allo stesso codice backend.
Priorità di Configurazione a Tre Livelli
# src/config.py
import os
app_name = os.getenv('APP_NAME', 'amal')
Livello 1: Configurazione esatta per app
config = load_json(f'config/{app_name}.json')
Livello 2: Configurazione per famiglia app (se manca la config esatta)
if not config:
family = app_name.split('_')[0] # da 'amal_beta' a 'amal'
config = load_json(f'config/{family}.json')
Livello 3: Configurazione di default
if not config:
config = load_json('config/default.json')
Configurazione per singola app (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
}
}
Risorse Condivise vs. Per-App
| Risorsa | Condivisa | Per-App | Motivo |
|---|---|---|---|
| Codice Lambda | ✓ Sì | ✗ No | Unico codice, branch per APP_NAME |
| Istanza RDS | ✓ Sì | ✗ No (tabelle prefissate) | Riduzione costi, backup unico |
| Contenuti S3 | ✗ No | ✓ Sì | Ogni app ha il proprio curriculum |
| AWS Cognito | ✓ Sì | ✗ No (via app_id) | Autenticazione centrale, differenzia tramite app_id |
| Analytics lake | ✓ Sì | ✗ No (partizionato per app) | Pipeline unificata, dati segregati |
| Firebase messaging | ✗ No | ✓ Sì | APNs + FCM specifici per app |
Frontend: Architettura Multi-App Flutter
Core Condiviso (packages/alphazed_common)
- Gestione stato (Riverpod)
- Gestione audio
- Libreria animazioni (integrazione Rive)
- Sistema design (colori, tipografia, widget)
- Flussi di autenticazione
- Client analytics
Strati Specifici di App (apps/amal, apps/thurayya, ecc.)
apps/
├── amal/
│ ├── lib/
│ │ ├── main.dart (punto di ingresso app)
│ │ ├── config/
│ │ │ ├── curriculum.json (struttura lezioni Amal)
│ │ │ ├── colors.dart (tema Amal)
│ │ │ └── characters.json (custom avatar)
│ │ └── screens/ (schermate specifiche Amal)
│ └── pubspec.yaml (dipendenze Amal)
│
├── thurayya/
│ ├── lib/
│ │ ├── main.dart (punto di ingresso differente)
│ │ ├── config/
│ │ │ ├── curriculum.json (struttura Juz Amma)
│ │ │ ├── colors.dart (tema Thurayya)
│ │ │ └── characters.json (custom avatar)
│ │ └── screens/ (schermate specifiche Thurayya)
│ └── pubspec.yaml (dipendenze Thurayya)
│
└── packages/
└── alphazed_common/ (codice condiviso)
Configurazione a Build-Time
# Build Amal
flutter build apk --dart-define=APP_NAME=amal --dart-define=CURRICULUM=amal_v3
Build Thurayya
flutter build apk --dart-define=APP_NAME=thurayya --dart-define=CURRICULUM=thurayya_juzamma
Al momento della compilazione, ogni app integra dati curriculum, colori e configurazioni propri. Un unico repository codice, molteplici binari compilati.
Deploy: Stack Indipendenti
Configurazione Serverless Framework
# serverless.yml
service: alphazed-backend
custom:
pythonRequirements:
dockerizePip: true
app_name: ${env:APP_NAME}
functions:
Funzioni deployate per ogni valore di APP_NAME
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:
# Ogni app ha il proprio gruppo log CloudWatch
${self:custom.app_name}LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/alphazed-${self:custom.app_name}
RetentionInDays: 30
Quando esegui un deploy con APP_NAME=amal, CloudFormation crea:
- Funzioni Lambda con prefisso
alphazed-amal-* - Rotte API Gateway sotto
/amal/* - Log CloudWatch sotto
/aws/lambda/alphazed-amal - Scalabilità e monitoraggio indipendenti
Perché è Importante
Per i team di prodotto
- Lancio nuove app in settimane, non mesi (infrastruttura condivisa)
- Parità funzionale: fix e miglioramenti istantanei su tutte le app
- A/B testing semplice su singole app prima del rollout
Per l'ingegneria
- Codice unico: un set di test e pipeline CI/CD
- Algoritmi di apprendimento condivisi: ripetizione spaziata e mix contenuti
- Efficienza operativa: un team per tutta l'infrastruttura
Per i costi
- RDS condiviso: frazione del costo rispetto a 7 database separati
- Cognito condiviso: un provider d'autenticazione per tutte le app
- Analytics unificato che supporta 7 app
- Risparmio stimato: 40.000-60.000 dollari/anno rispetto a back-end separati
Sfide e Soluzioni
Sfida 1: Conflitti nello schema database
- Amal richiede tabella amal_user_memory
- Thurayya richiede tabella thurayya_user_memory (campi diversi per memorizzazione Corano)
- Soluzione: migrazioni consapevoli dell'app. Esempio: migrations/001_amal_user_memory.sql eseguita solo con APP_NAME=amal
Sfida 2: Set di funzionalità diverse
- Thurayya ha feedback tajweed; Amal no
- Amal ha giochi di fisica; Thurayya no
- Soluzione: feature flags in configurazione (enable_tajweed_feedback, enable_physics_games)
Sfida 3: Scalabilità indipendente
- Picco traffico su Amal, Thurayya tranquilla
- Pool Lambda condiviso, concorrenza in competizione
- Soluzione: politiche CloudWatch di scalabilità per app. Se Lambda amal-* raggiunge limite, si scala automaticamente e indipendentemente
FAQ
D: Condividere una base di codice non crea accoppiamento tra app?
R: No. Il codice di ogni app è in directory separate (apps/amal, apps/thurayya). Il codice condiviso è in packages/alphazed_common e versionato esplicitamente. Un accoppiamento stretto sarebbe una cattiva pratica e viene rilevato nelle revisioni.
D: E se una app necessita di una modifica API incompatibile?
R: Versioniamo le API per app: /amal/v1/*, /thurayya/v1/*. Le app si aggiornano indipendentemente. Le vecchie versioni restano attive per 12 mesi per permettere l’aggiornamento.
D: Le app possono condividere gli utenti?
R: Non di default. Ogni app ha la propria tabella utenti (con prefisso). Se un genitore vuole abbonarsi a entrambe (Amal e Thurayya), deve creare account separati (o potremmo aggiungere una funzionalità "family" in futuro).



