era una volta…
…un progetto software in fase di kick-off. Il team era in tripudio, perché gli era stato affidato un progetto per un portale di eCommerce integrato con la logistica di Magazzino della durata di uno spaventilione gg/uomo; ma era allo stesso tempo anche molto in apprensione per il fatto che era il suo primo progetto su piattaforma Java Enterprise.
Gli skill del team erano molto ferrati sul DBMS Oracle e finora aveva sviluppato e consegnato con discreto successo applicazioni DB centriche, dove la logica di business era codificata nelle tabelle e la GUI era affidata a Oracle Forms, che non prevede i cosiddetti tiers tipici delle applicazioni enterprise.
Il tecnico di punta di questo team è Guido: è un architetto DB con esperienza pluriennale nello sviluppo di applicazioni su piattaforma Oracle. Il suo motto: “La purezza del dato sopra ogni cosa“.
Giorno 0: Contaminazione
Per mettere il team in condizione di affrontare con più serenità il suo primo progetto su Java Enterprise il coach del team, Massimo, introduce nel gruppo di lavoro Fabiana, una Tech Leader esperta della piattaforma JavaEE5 e specializzata su JPA. Il suo motto: “Il Database serve per persistere i dati, non la logica di business“.
Giorno 1: Da dove partiamo?
Fabiana e Guido sono fermamente convinti, nonostante l’approccio completamente diverso, che dovranno collaborare e arrivare a compromessi per il buon esito del progetto. La prima domanda che si pongono è: “da dove partiamo?”
Fabiana: “Dato che usiamo Hibernate, un ORM, è naturale partire dalla progettazione delle classi Java e fare generare le tabelle di conseguenza”
Guido: “Ma noi abbiamo sempre prima progettato il Database e poi le form di conseguenza!”
Massimo: “E’ vero. Non so se una delle due strade è preferibile all’altra, probabilmente dipende dagli skill del team; sono invece abbastanza sicuro che sia meglio che la progettazione del modello di classi e del database sia il più coerente possibile, per evitare il fenomeno dell’Impedance mismatch“
Fabiana: “Per fortuna non dobbiamo lavorare con una base di dati legacy…”
Massimo: “Già…potremmo lavorare insieme a un diagramma concettuale database che prevede però alcuni concetti tipici della progettazione ad oggetti, quali l’ereditarietà, da utilizzare in qualunque circostanza si riveli utile”
Guido e Fabiana: “Che stiamo aspettando?”
Giorno 2: Chiave di Volta
I primi dolori cominciano già quando si deve scegliere la chiave primaria delle entità in gioco.
Guido: “Prevederei chiavi logiche, ovvero quando l’entità nasce dalla composizione di altri concetti la sua chiave è la composizione delle loro chiavi”
Fabiana: “Sì, so che di solito è buona pratica lato database operare in questo modo…tuttavia lato applicativo ci converebbe non estremizzare: le chiavi fisiche (campi autoincrementanti o gestiti con sequence per i DBMS che li supportano) sono molto comode, perché semplificano la codifica dello strato di persistenza”
Massimo: “In questo caso direi che non ha senso complicarci la vita per una questione di principio: vogliamo consegnare al cliente un software di valore quanto prima, seguirei quindi l’approccio di Fabiana. Sempre in quest’ottica non seguirei una normalizzazione del database scrupolossima: nei casi dove un campo denormalizzato ci migliori le performance lo prevederei…”
Guido: “Ok…c’è da dire che con le chiavi fisiche le query saranno un po’ più corte :-)”
Giorno 3: Tracciatura, che tortura!
Massimo: “Il cliente ci ha espresso chiaramente la necessità di tracciare cosa avviene nel sistema: ad esempio quando un cliente del portale cambia l’indirizzo di spedizione predefinito bisogna mantenere l’informazione dei precedenti, per poter capire cosa è successo in tutti i casi in cui ci siamo problemi”
Guido: “In pratica potremmo mettere sulla tabella associativa Cliente-Indirizzo due campi data_inizio e data_fine: la tabella andrà a contenere tutti gli indirizzi che il cliente ha avuto associati nei vari slot temporali. Questa soluzione potremmo applicarla su tutte le tabelle associative, così non ci perdiamo nulla di quello che cambia…”
Fabiana: “Beh, è una soluzione…vi allerto però di qualche inconveniente: innanzitutto le tabelle associative pure nello strato di persistenza Java non sono mappate da entità, perché non lo sono; questo è dovuto al fatto che il modello entità relazione, su cui si basano tutti i database relazionali, non consente le relazioni n-n e per realizzarle viene utilizzata una tabella associativa; questo è una implementazione fisica del database, ma non vorremmo che per motivi di tracciatura ci trovassimo un modello concettuale “sporcato” dalle limitazioni implementative del database fisico; questo comunque sarebbe il male minore: infatti ogni volta che entriamo nella tabellaCliente-Indirizzo
siamo in questo modo costretti a filtrare in base alla data odierna e questo complica le cose e può essere eventualmente meno performante”
Guido: “Potremmo lasciare che la tabella associativa rimanga pura e che contenga l’indirizzo di spedizione attualmente in vigore e prevedere una nuova tabella,Cliente-Indirizzo-Log
, che contenga le associazioni precedenti, partizionate temporalmente grazie ai due campidata_inizio
edata_fine
“
Massimo: “Mi sembra ottimo: tanto il cliente ci ha detto che le informazioni di tracciatura non saranno quasi mai mostrate a video, quindi probabilmente è corretto metterle in una tabella separata, in modo che non vada a minare le performance dell’applicativo…inoltre essendo una tabella separata possiamo tranquillamente gestire in modo autonomo logiche di storicizzazione…”
Guido: “…che saranno sicuramente necessarie: sulle tabelle di tracciatura i dati crescono molto in fretta! Se per Fabiana va bene proporrei anche di liberare l’applicativo dalla responsabilità di popolare questa tabella: potremmo creare un trigger sulla tabellaCliente-Indirizzo
che ad ogni variazione alimenta coeremente la tabellaCliente-Indirizzo-Log
”
Fabiana: “Mi sembra un’ottima idea! Così facciamo fare anche al DB la sua parte di lavoro. I trigger talvolta creano problemi con i livelli di cache JPA, ma dato che in questo caso si va ad operare su una entità che lato applicativo non viene neanche modelizzata direi che siamo tranquilli!”
Massimo: “Perfetto! Complimenti ragazzi, problema risolto!”
Giorno 4: svoltiamo pagina nelle performance
Massimo: “Sono un po’ preoccupato per le performance dell’applicazione. In particolare vedo due problemi: la tabella
Articoli
ad esempio conterrà un elevato numero di righe e la ricerca sull’applicativo web potrebbe risentirne molto; l’altro aspetto è questo: stiamo cercando di portare la logica di business sullo strato EJB dell’applicativo Java, ma ci sono casi in cui le operazioni da fare implicano operazioni massive sui dati e su questo tipo di operazioni delegare al database sarebbe sicuramente più performante…idee?”
Fabiana: “E’ buona pratica nelle applicazioni Web paginare i risultati, ovvero mostrare 50-100 risultati nella prima pagina: per visualizzare i successivi l’utente clicca sul numerino della pagina seguente. Oltre a migliorare le performance della query si riesce anche a migliorare l’occupazione della memoria in sessione”
Massimo: “Ok, il nostro cliente non c’è abituato, tuttavia mi sembra una soluzione sensata: se viene effettuta una ricerca che fornisce un alto numero di risultati sarebbe comunque impossibile per lui analizzarli tutti assieme…troppa informazione equivale a nessuna informazione, no?”
Guido: “Sono d’accordo. Io sto lavorando con Fabiana per impostare gli indici corretti sulla tabellaArticoli
in base ai parametri di ricerca che saranno presenti in interfaccia, in modo da ottimizzare le query sul database”
Fabiana: “Per quanto riguarda le performance di operazioni massive sui dati penso sia giusto delegarle al database, utilizzando delle stored procedure; se il risultato della elaborazione sono dati da mostrare a video potremmo prevedere parametri aggiuntivi da passare alla stored procedure ai fini di paginare il risultato anche in questo caso”
Guido: “Non avrei mai creduto che tu proponessi questo, ma mi sembra sensato :-)”
Massimo: “Anche nella stesura di queste stored procedure possiamo comunque utilizzare best practice di modularità, in modo da facilitare la manutenzione e il test, che ne pensate?”
Guido: “Direi di sì! Una buona norma non è detto si debba applicare solo in Java :-)”
Giorno 5: enumeriamo i vantaggi
Massimo: “Guido, ho visto che ci sono molte tabelline che contengono poche righe e che sembrano abbastanza statiche; ad esempio la tabella
Tipo-Pagamento
che contiene 0 – “CARTA_CREDITO”, 1 – “PAYPAL”, 2 – “CONTRASSEGNO”, 3 – “BONIFICO”, siamo sicuri che sia necessaria? Ci risparmieremmo qualche join se la togliessimo e delegassimo la gestione all’applicativo”
Guido: “Mmm…però se la cabliamo nel codice quando nasce una nuova tipologia di pagamento supportata siamo costretti a fare un deploy, invece così basta una insert…”
Fabiana: “Beh, se nasce una nuova tipologia di pagamento da supportare sono sicura che aggiungere una nuova riga in tabella non sarebbe sufficiente: lato applicativo mancherebbe comunque tutta la sua parte di gestione della nuova tipologia; quindi un nuovo deploy è assicurato, ma anche diversi giorni di sviluppo :-)”
Guido: “E’ vero. Tuttavia ora nella tabellaOrdine
abbiamo un campo in foreign key verso la tabellaTipo-Pagamento
, dopo siamo sicuri che il valore inserito in tale campo sia tra quelli consentiti?”
Fabiana: “Utilizzando gli enum Java e JPA siamo assolutamente sicuri…ti mando un po’ di documentazione a riguardo…”
Guido: “Ok, grazie…e comunque possiamo prevedere un check constraint sul campo in modo da avere un controllo lato database; mi rimane un ultimo dubbio: la tabella forniva una sorta di documentazione per l’utente più esperto in grado di accedere al DB tramite strumenti friendly…con una select sulla tabellaTipo-Pagamento
sapeva che il tipo di pagamento 3 era il BONIFICO; possiamo lasciargli in qualche modo questa informazione?”
Fabiana: “Sicuro! JPA consente di mappare i campi enumerati nelle entità non solo con l’ordinale, ma anche con iltoString
, senza sforzo aggiuntivo. Nel campo della tabellaOrdine
possiamo quindi inserire “CARTA_CREDITO”, “PAYPAL”, “CONTRASSEGNO”, “BONIFICO” anziché 0, 1 , 2, 3. Che ne dici?”
Guido: “Mi hai convinto!”
Conclusioni
Ci piacerebbe dirvi che alla fine del progetto, consegnato in tempo e con massima soddisfazione del cliente, Massimo ha avuto una promozione e Fabiana e Guido si sono addirittura fidanzati!
Beh, questo sconfinerebbe davvero nel “E vissero felici e contenti“, tuttavia dalla storia di questo progetto abbiamo appreso come la progettazione di un dababase utilizzato come supporto di persistenza di una applicazione JavaEE richiede tutta una serie di accortezze e di best practice che sono difficili da reperire nei manuali, ma si acquisiscono solo con l’esperienza. E voi vi siete riconosciuti nei personaggi della nostra storia? Volete condividere il vostro racconto? Lasciateci pure le vostre considerazioni nei commenti!
Alla prossima!
Disclaimer
Nessun amministratore DB o architetto JavaEE è stato maltrattato nella stesura di questo post. I nomi e i fatti narrati sono assolutamente fittizi e utilizzati per scopi didattici.