Schema del database
Agon usa SQLite (modalità WAL) come database locale. Tutti i valori datetime sono conservati in UTC (senza fuso orario).
Diagramma entità-relazione
erDiagram
users {
int id PK
string email UK
string password_hash
string full_name
string role
bool is_active
datetime created_at
datetime updated_at
}
clients {
int id PK
int location_id
string email UK
string password_hash
string full_name
string phone
date dob
string notes
string push_token
bool is_active
datetime created_at
datetime updated_at
}
instructors {
int id PK
int user_id FK
int location_id
string bio
string specialties
datetime created_at
datetime updated_at
}
class_templates {
int id PK
int location_id
string name
string description
int duration_minutes
int default_capacity
string color
int default_instructor_id FK
bool is_active
datetime created_at
datetime updated_at
}
scheduled_classes {
int id PK
int template_id FK
int instructor_id FK
int location_id
datetime starts_at
datetime ends_at
int capacity
string status
string meeting_url
datetime created_at
datetime updated_at
}
bookings {
int id PK
int client_id FK
int scheduled_class_id FK
int location_id
string status
datetime created_at
datetime updated_at
}
checkins {
int id PK
int booking_id FK
int client_id FK
int scheduled_class_id FK
int checked_in_by FK
string method
datetime checked_in_at
}
waitlist {
int id PK
int client_id FK
int scheduled_class_id FK
int position
string status
datetime expires_at
datetime created_at
datetime updated_at
}
membership_types {
int id PK
int location_id
string name
string type
float price
string currency
int credits
int duration_days
bool is_active
datetime created_at
datetime updated_at
}
memberships {
int id PK
int client_id FK
int membership_type_id FK
int location_id
string status
date starts_at
date expires_at
int credits_remaining
int credits_used
string stripe_subscription_id
datetime created_at
datetime updated_at
}
payments {
int id PK
int client_id FK
int membership_id FK
float amount
string currency
string status
string provider
string provider_payment_id
datetime paid_at
datetime created_at
datetime updated_at
}
locations {
int id PK
string name
string address
string phone
bool is_active
datetime created_at
datetime updated_at
}
studio_settings {
int id PK
string studio_name
string timezone
string currency
string stripe_public_key
string stripe_secret_key
string cloudflare_tunnel_token
int booking_cancellation_hours
bool waitlist_enabled
datetime created_at
datetime updated_at
}
consent_log {
int id PK
int client_id FK
string type
bool granted
string ip_address
datetime created_at
}
email_templates {
int id PK
string name
string subject
string body_html
string variables
bool is_active
datetime created_at
datetime updated_at
}
email_event_assignments {
int id PK
string event_type
int template_id FK
}
smart_lists {
int id PK
string name
string filters_json
datetime created_at
datetime updated_at
}
users ||--o{ instructors : "has profile"
clients ||--o{ bookings : "makes"
clients ||--o{ memberships : "holds"
clients ||--o{ waitlist : "joins"
clients ||--o{ payments : "makes"
clients ||--o{ consent_log : "logs"
scheduled_classes ||--o{ bookings : "receives"
scheduled_classes ||--o{ waitlist : "has"
scheduled_classes ||--o{ checkins : "records"
class_templates ||--o{ scheduled_classes : "spawns"
instructors ||--o{ scheduled_classes : "teaches"
instructors |o--o{ class_templates : "default for"
bookings ||--o| checkins : "has"
memberships ||--o{ payments : "paid via"
membership_types ||--o{ memberships : "defines"
email_templates |o--o{ email_event_assignments : "assigned to"
Tabelle principali
| Tabella | Scopo |
|---|---|
users | Account dello studio manager e degli istruttori (credenziali di accesso) |
clients | Clienti della palestra che prenotano lezioni tramite l'app mobile |
instructors | Profilo uno-a-uno collegato a una riga users |
class_templates | Definizione riutilizzabile di lezione (Yoga, HIIT, Pilates…) |
scheduled_classes | Un'occorrenza specifica di un tipo di lezione in una data/ora |
bookings | La prenotazione di un cliente per una lezione pianificata |
checkins | Registro di presenza confermata per una prenotazione |
waitlist | Voce in coda quando una lezione è al completo |
memberships | L'abbonamento o pacchetto crediti attivo di un cliente |
membership_types | Modello per piani ricorrenti o pacchetti crediti |
payments | Registri di pagamento (Stripe o manuale) |
locations | Sede fisica dello studio (V1: sempre id=1) |
studio_settings | Riga singleton con la configurazione dello studio |
consent_log | Eventi di consenso GDPR |
email_templates | Corpi delle email transazionali personalizzati |
email_event_assignments | Mappa gli eventi del ciclo di vita ai template personalizzati |
smart_lists | Query di filtro clienti salvate per messaggistica mirata |
Note di progettazione
Datetime UTC senza fuso orario
Tutte le colonne DateTime conservano valori UTC senza informazioni di fuso orario. Il livello applicativo confronta sempre con utcnow() da app.utils. Non inserire mai datetime con fuso orario nel database.
Convenzione location_id
Ogni entità di business porta un campo location_id (predefinito 1). La sede 1 (Main Studio) viene creata al momento della migration. Questa colonna è il gancio per il supporto multi-sede della V2 — nessuna modifica allo schema necessaria.
Eliminazione soft vs hard
- Eliminazione soft (status/is_active): clienti, istruttori, tipi di lezione, tipi di abbonamento, prenotazioni, abbonamenti, lezioni pianificate.
- Eliminazione hard: solo su richiesta esplicita di cancellazione GDPR. I dati personali vengono anonimizzati sul posto; le righe di cronologia prenotazioni/pagamenti vengono conservate per finalità di audit.
Vedi ARCHITECTURE.md nel codice sorgente per la strategia completa di eliminazione e le regole di cascata.
Indici composti
I pattern di query critici per le prestazioni sono coperti da indici composti:
| Indice | Colonne | Pattern di query |
|---|---|---|
idx_booking_class_status | scheduled_class_id, status | Tutte le prenotazioni confermate per una lezione |
idx_booking_client_status | client_id, status | Cronologia prenotazioni del cliente |
idx_membership_client_status | client_id, status | Ricerca abbonamento attivo del cliente |
idx_class_starts_at_location_status | starts_at, location_id, status | Query di calendario per intervallo di date |