Applicazione web per la gestione di una libreria personale: gli utenti possono catalogare i libri, tracciare il progresso di lettura, scrivere recensioni, organizzare i libri con tag e preferiti, crearsi sfide di lettura personali, e ricevere consigli di lettura.
- Cos'è il progetto
- Architettura generale
- Come funziona — flusso utente
- Backend
- Frontend
- Sistema di raccomandazione AI
- Database
- DevOps — Pipeline CI/CD
- Avvio del progetto
- Struttura delle cartelle
Libreria Virtuale è un'applicazione full-stack composta da tre servizi indipendenti che comunicano tra loro:
| Servizio | Tecnologia | Ruolo |
|---|---|---|
| Backend | Java 21 + Spring Boot | Logica di business, autenticazione, API REST, database |
| Frontend | React + Vite | Interfaccia utente |
| Recommendation Service | Python + FastAPI | Motore AI che genera consigli di lettura personalizzati |
I tre servizi sono containerizzati con Docker e orchestrati con Docker Compose, con una pipeline CI/CD automatizzata che costruisce, testa, analizza e pubblica ogni componente ad ogni modifica del codice.
- Account: registrazione, login, recupero password, gestione credenziali
- Catalogo libri: ricerca su Google Books, importazione nel catalogo personale
- Libreria personale: stato di lettura (da leggere / in lettura / letto), tracciamento pagine, preferiti, wishlist
- Tag: organizzazione libera dei libri con etichette personalizzate
- Recensioni: valutazione a stelle e commento, solo sui libri completati
- Sfide di lettura: obiettivi personali per numero di libri o pagine, con scadenza
- Dashboard statistiche: libri letti, pagine lette, media voti, progressi
- Consigli AI: suggerimenti di lettura basati sui libri valutati positivamente
Perché tre servizi separati invece di uno solo?
- Il backend Java gestisce tutto ciò che riguarda dati, sicurezza e regole di business.
- Il frontend React si occupa solo della presentazione: parla con il backend tramite API REST.
- Il servizio di raccomandazione è isolato in Python perché le librerie di Machine Learning (scikit-learn) e l'ecosistema di data science sono nativi in quel linguaggio. Tenerlo separato significa anche che può essere aggiornato, scalato o sostituito senza toccare il resto del sistema.
- L'utente si registra con username, email e password
- Il backend salva la password hashata
- Al login, il backend restituisce un token JWT, che il frontend salva e invia automaticamente in ogni richiesta successiva.
- Se l'utente dimentica la password, può reimpostarla dimostrando di conoscere username + email (senza bisogno di un'email di conferma)
- L'utente cerca un titolo dalla barra di ricerca (es. "Harry Potter")
- Il backend interroga Google Books API e restituisce i risultati
- Quando l'utente seleziona un libro, questo viene importato nel catalogo globale dell'applicazione.
- Il libro viene quindi collegato alla libreria personale dell'utente, con stato iniziale "Da leggere"
- L'utente può cambiare lo stato di un libro (Da leggere → In lettura → Letto) e aggiornare il numero di pagine lette in qualsiasi momento
- Se le pagine lette raggiungono il totale del libro, lo stato passa automaticamente a "Letto"
- Una volta completato, l'utente può scrivere una recensione con voto da 1 a 5 stelle (il backend impedisce di recensire libri non ancora finiti)
- L'utente crea una sfida con un obiettivo (es. "12 libri nel 2026" o "5000 pagine entro dicembre") e una scadenza
- Il progresso viene calcolato automaticamente confrontando i libri letti/le pagine segnate con il periodo della sfida — non è l'utente a dover aggiornare manualmente l'avanzamento
- Dopo aver recensito alcuni libri, nella Dashboard appare la sezione "Consigliati per te"
- Il backend manda al servizio Python i libri valutati dall'utente; il servizio Python restituisce una lista di suggerimenti con un punteggio di affinità e una breve spiegazione del perché sono stati proposti (vedi sezione 6)
Tecnologie: Java 21, Spring Boot 3.3, Spring Data JPA, Spring Security, JWT, Flyway, PostgreSQL.
Controller → riceve le richieste HTTP, valida l'input
Service → contiene la logica di business
Repository → interroga il database (Spring Data JPA)
Entity → rappresentano le tabelle del database
DTO → oggetti scambiati con il frontend (mai le Entity dirette)
Questa separazione (pattern standard nelle applicazioni Spring Boot) permette di cambiare un livello senza impattare gli altri — per esempio, il formato delle risposte JSON (DTO) può evolvere senza toccare le tabelle del database (Entity).
| Area | Endpoint | Descrizione |
|---|---|---|
| Autenticazione | POST /api/auth/registrazione |
Crea un nuovo account |
POST /api/auth/login |
Restituisce un token JWT | |
POST /api/auth/password-dimenticata |
Reset password senza email | |
PUT /api/auth/credenziali |
Modifica email/password | |
DELETE /api/auth/account |
Elimina l'account | |
| Catalogo | GET /api/libri/ricerca-online |
Cerca su Google Books |
POST /api/libri/importa |
Importa un libro nel catalogo | |
| Libreria | GET /api/libreria |
Lista i libri dell'utente |
PATCH /api/libreria/{id}/stato |
Cambia stato di lettura | |
PATCH /api/libreria/{id}/progresso |
Aggiorna le pagine lette | |
PATCH /api/libreria/{id}/preferito |
Segna/togli dai preferiti | |
| Tag | POST /api/tag |
Crea un tag personalizzato |
| Recensioni | POST /api/recensioni |
Pubblica una recensione |
| Sfide | POST /api/challenge |
Crea una sfida di lettura |
| Statistiche | GET /api/statistiche |
Dashboard riepilogativa |
| AI | GET /api/raccomandazioni |
Consigli di lettura personalizzati |
La documentazione interattiva completa (Swagger) è disponibile, ad
applicazione avviata, su http://localhost:8080/swagger-ui.html.
- Le password non sono mai salvate in chiaro (hash BCrypt)
- L'autenticazione è stateless: ogni richiesta porta il proprio token JWT, il server non mantiene sessioni in memoria.
- Ogni utente può accedere solo ai propri dati: il backend verifica sempre che la risorsa richiesta (libro in libreria, tag, recensione, sfida) appartenga all'utente autenticato.
Le modifiche allo schema del database non vengono fatte "a mano": ogni
cambiamento (nuova tabella, nuova colonna, correzione di una vista) è
scritto in un file SQL numerato (V1__..., V2__..., ecc.) dentro
backend/src/main/resources/db/migration/. All'avvio, Flyway
applica automaticamente solo le migrazioni non ancora eseguite,
garantendo che ogni ambiente (sviluppo, test, produzione) abbia
esattamente lo stesso schema, nello stesso ordine.
Tecnologie: React 19, Vite, React Router, Tailwind CSS, Axios.
src/
├── pages/ Una pagina per ogni "schermata" (Dashboard, Libreria, Sfide, ...)
├── components/ Pezzi di interfaccia riusabili (card, modali, navbar)
├── api/ Funzioni che chiamano il backend (una per area: auth, libreria, ...)
├── context/ Stato condiviso tra pagine (utente loggato, notifiche)
└── routes/ Protezione delle pagine (alcune richiedono login)
- Autenticazione persistente: il token JWT è salvato nel browser, così l'utente resta loggato anche chiudendo e riaprendo la pagina
- Notifiche non invasive (toast): qualsiasi errore di rete (es. sessione scaduta, server irraggiungibile) mostra un avviso in basso allo schermo, senza bloccare l'interfaccia
- Niente popup per la ricerca libri: cercare un libro nuovo apre
una pagina di risultati dedicata (
/cerca) - Aggiornamento in tempo reale: aggiungere un libro da qualunque punto dell'app (anche dalla barra di ricerca globale) aggiorna automaticamente le altre pagine aperte.
Tecnologie: Python 3.12, FastAPI, scikit-learn.
Con pochi dati disponibili (un singolo utente, o pochi utenti con gusti molto diversi), un sistema di raccomandazione collaborativo ("agli utenti che hanno letto X è piaciuto anche Y") non ha senso: serve un grande numero di persone che si sovrappongono sugli stessi libri. Per questo motivo è stato scelto un approccio content-based: il sistema guarda cosa contiene un libro (genere, autore, temi della trama) e cerca libri con contenuti simili — funziona fin dal primo libro recensito.
- Raccolta dei gusti: il backend Java manda al servizio Python tutti i libri che l'utente ha recensito, con relativo voto
- Costruzione del "profilo gusti": il servizio Python individua i generi e gli autori più ricorrenti tra i libri votati meglio, e costruisce un vettore numerico che rappresenta i temi più caratteristici delle trame apprezzate
- Ricerca dei candidati: usando generi e autori preferiti come parole di ricerca, il servizio interroga Google Books per trovare libri potenzialmente interessanti
- Calcolo dell'affinità: ogni candidato trovato viene confrontato con il profilo gusti tramite cosine similarity (una misura standard di quanto due "direzioni" nello spazio dei temi siano simili), combinata con un bonus se genere o autore coincidono esattamente con quelli preferiti
- Filtri di qualità: vengono scartati i libri già posseduti dall'utente, i generi non narrativi (biografie, saggi critici sull'autore — utili a chi cerca informazioni su uno scrittore, ma non un libro simile a quelli amati), e i suggerimenti con punteggio troppo basso per essere significativi
- Risposta: la lista finale, ordinata per punteggio
Il motore di raccomandazione potrebbe in teoria vivere dentro il backend Java stesso, ma tenerlo come servizio indipendente offre vantaggi concreti:
- Le librerie di calcolo scientifico (scikit-learn, numpy) sono native in Python, non serve "tradurle" o trovare equivalenti Java
- Può essere aggiornato, sperimentato o sostituito (es. con un modello più sofisticato in futuro) senza toccare una riga del backend Java
- Comunica con il resto del sistema tramite una semplice API HTTP, lo stesso principio usato da sistemi di raccomandazione reali in produzione su scala molto più grande (es. Netflix, Amazon)
PostgreSQL, con schema versionato tramite Flyway. Tabelle principali:
| Tabella | Contenuto |
|---|---|
utente |
Account registrati |
libro |
Catalogo globale dei libri (condiviso) |
libreria_utente |
La copia personale di un libro nella libreria di un utente (stato, pagine, preferito, wishlist) |
recensione |
Voti e commenti, collegati a una voce di libreria |
tag / tag_libro |
Etichette personalizzate e loro associazione ai libri |
challenge |
Sfide di lettura create dagli utenti |
Due vincoli di integrità sono applicati direttamente nel database tramite trigger:
- una recensione può essere inserita solo se il libro è segnato come "Letto"
- un tag può essere associato solo a un libro nella libreria dello stesso utente che ha creato il tag
Ad ogni push sul branch main, GitHub Actions esegue
automaticamente una pipeline composta da 4 job: 3 in parallelo + 1 finale.
┌───────────┐ ┌────────────┐ ┌─────────────────────────┐
│ Backend │ │ Frontend │ │ Recommendation Service │
│ (Java) │ │ (React) │ │ (Python) │
└─────┬──────┘ └─────┬──────┘ └────────────┬─────────────┘
│ │ │
└────────────────┴───────────┬───────────┘
▼
┌──────────────────┐
│ SonarCloud │
│ (analisi qualità) │
└──────────────────┘
Ogni job dei tre servizi esegue, in sequenza:
- Build — compila/installa le dipendenze
- Test automatici — esegue la suite di test (JUnit per il backend, Vitest per il frontend, pytest per il servizio Python)
- Snyk — scansiona le dipendenze del progetto in cerca di vulnerabilità di sicurezza conosciute
- Build e pubblicazione immagine Docker — l'immagine viene costruita e pubblicata su Docker Hub, pronta per essere scaricata e avviata ovunque
Il quinto job (SonarCloud) parte solo dopo che tutti gli altri tre sono completati, ed esegue un'analisi statica del codice su backend, frontend e servizio Python insieme, producendo un report unico con metriche di sicurezza, manutenibilità, copertura dei test e duplicazione del codice.
- Test automatici: garantiscono che una modifica non rompa una funzionalità esistente, prima ancora che arrivi in produzione
- Snyk: individua dipendenze di terze parti con vulnerabilità note.
- SonarCloud: misura la qualità del codice in modo oggettivo (bug potenziali, codice duplicato, complessità) — utile sia per individuare problemi sia per tenere traccia di come il codice evolve nel tempo
- Docker + Docker Hub: rendono l'applicazione portabile: chiunque, con Docker installato, può avviare l'intero sistema con un solo comando, senza installare Java, Node, Python o PostgreSQL sulla propria macchina
Con Docker installato:
docker compose upQuesto comando scarica automaticamente le immagini già pubblicate su Docker Hub e avvia tutti i servizi (database, backend, frontend, servizio di raccomandazione) collegati tra loro. L'applicazione sarà disponibile su http://localhost:5173.
Ogni servizio ha il proprio README con le istruzioni dettagliate:
.
├── backend/ Spring Boot — API, logica, database
│ ├── src/main/java/com/libreria/
│ │ ├── controller/ Endpoint REST
│ │ ├── service/ Logica di business
│ │ ├── repository/ Accesso al database
│ │ ├── entity/ Tabelle del database
│ │ ├── dto/ Oggetti scambiati con il frontend
│ │ ├── security/ Autenticazione JWT
│ │ └── client/ Chiamate verso Google Books e il servizio AI
│ └── src/main/resources/db/migration/ Migrazioni Flyway (schema DB)
│
├── frontend/ React — interfaccia utente
│ └── src/
│ ├── pages/ Schermate dell'applicazione
│ ├── components/ Componenti riusabili
│ ├── api/ Chiamate al backend
│ └── context/ Stato condiviso (utente, notifiche)
│
├── recommendation-service/ FastAPI — motore di raccomandazione AI
│ └── app/
│ ├── main.py Endpoint dell'API
│ ├── profilo_gusti.py Costruzione del profilo utente (TF-IDF)
│ ├── motore_raccomandazione.py Ricerca e ranking dei candidati
│ └── google_books_client.py Chiamate a Google Books
│
├── docker-compose.yml Orchestrazione dei servizi
└── .github/workflows/ci-cd.yml Pipeline CI/CD