Come Creare una Piattaforma Multi-App da un Singolo Codice
5 min di letturaMohammad Shaker

Come Creare una Piattaforma Multi-App da un Singolo Codice

Alphazed gestisce 7+ app educative con un unico backend e Flutter, ognuna con database e contenuti separati.

Engineering

Risposta rapida

Alphazed gestisce 7+ app educative con un unico backend e Flutter, ognuna con database e contenuti separati.

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

RisorsaCondivisaPer-AppMotivo
Codice Lambda✓ Sì✗ NoUnico 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).

Articoli correlati