venerdì 23 febbraio 2007

Non ibernatemi


David Blaine, nel novembre del 2000, si fece rinchiudere vivo in un blocco di ghiaccio da 6 tonnellate in cui stette in piedi per oltre 61 ore, in bella vista a Manhattan's Times Square indossando solo un paio di pantaloni e degli stivali. Lo stunt è noto come Frozen in Time e purtroppo non ne esistono immagini gratuite, quindi ho usato una controfigura e voi dovrete andare sui diversi siti per vedervi quelle vere.
Non ripetetelo a casa, è pericoloso :)


In realtà è un'altra l'ibernazione di cui vorrei parlare, quella che da il nome, appunto, a Hibernate, ovvero la gestione della persistenza di oggetti su RDBMS, con relativi problemi di mapping tra queste due diverse tecnologie, noto anche con la sigla ORM, Object Relational Mapping.

Purtroppo non c'è stato verso di semplificare e rendere più leggero questo post: è una delle cose più pesanti che abbia mai scritto e cita altre fonti che farebbero sembrare dei fumetti i testi di Dostoevskij. Non sto scherzando, siete avvisati.



The Vietnam of Computer Science è un post di Ted Neward , oramai citato ovunque si parli di ORM (Object Relational Mapping) seriamente e senza voler proporre un prodotto, in cui vengono chiaramente espressi molti problemi di questa tecnica, in modo molto più colorito di quanto io possa fare (e, fatto curioso, decisamente dilungandosi di più di quanto io possa nemmeno immaginare di fare: se lo leggerete, imparerete ad apprezzare molto le mie doti di sintesi).

Credo che il titolo basti da solo ad indicare la gravità di quanto Ted pensa a proposito dell'ORM, ma vi anticipo subito che, seppur io fossi già d'accordo prima di leggerlo (pur non avendolo mai razionalizzato in modo così completo, prima), ci sono anche posizioni più favorevoli (per es. questa e quest'altra, entrambe di un professionista di grande esperienza e notevoli capacità, con cui ho avuto il piacere di lavorare nel primo progetto della mia carriera).

C'è, onestamente, la possibilità che le diverse posizioni possano derivare dalle personali modalità di crescita tecnica dei singoli: nel mio caso, avendo imparato prima ad utilizzare gli RDBMS e poi le tecnologie OO, tendo a non considerare un problema il dover trattare con il DB, anzi, parto sempre a progettarlo per primo, per mettere le fondamenta ai miei progetti (pur cercando di mantenere disaccoppiati il più possibile i due modelli).

Oggettivamente, comunque, ci sarà pure un motivo se Hibernate si è così diffuso ed ha raggiunto la sua versione 3.2.2 (ad oggi) e se la stessa SUN ha finalmente buttato i vecchi Entity Beans e ha implementato un nuovo tipo di persistenza che tratta anche i POJO, nell'ultima Java EE 5 Platform (a proposito, è mai possibile che non ci sia ancora una sigla più corta, ufficiale, da sostituire al vecchio J2EE? Se la conoscete, per favore, indicatemela).

Certo, il motivo del successo di queste soluzioni potrebbe essere l'intenzione di ridurre i costi (?), evitando di dover gestire a mano l'integrazione via JDBC o ADO ed il relativo mapping e di dover mantenere forti competenze sia su una tecnologia OO che su uno o più RDBMS.
O, ma speriamo che si tratti di casi rari ed isolati, potrebbe essere dovuto alla pigrizia del non voler gestire direttamente il DB e le sue diverse casistiche.

Non voglio rebloggare Ted Neward (peraltro, vista la lunghezza e completezza del suo articolo, sarà comunque difficile non farlo, dovessi anche mettermi a parlare di tecniche di ricamo ai tempi di Napoleone): leggetevelo da soli che vi fa bene (potete saltare la digressione sulla storia della guerra, IMHO).

Vorrei solo riassumere, in modo meno formale e più legato a recenti casistiche reali di progetto, quelli che sono i pro ed i contro, per vedere se e come si possano riuscire a conciliare.

Iniziamo col dire che un RDBMS è più dello schema dei dati in esso contenuto, ma è un engine che offre una serie di servizi (indicizzazione, query, atomicità delle transazioni, etc.) che non è, a mio avviso, pensabile poter perdere e/o dover riscrivere per adattarli al nostro Object Model.

Bisognerà, poi, dire due cose sul design pattern che sta alla base del problema incriminato, in questo caso il DAO.
Dobbiamo cercare di non lasciarci ingannare dall'impostazione dei blueprint di SUN, che vede il DAO, fin da subito, come una soluzione alla necessità di rendere persistenti gli oggetti: inteso in questo senso ricadrebbe subito nel problema ORM e non mi resterebbe che rimandarvi alle rovine di Saigon (anche se il DAO risolve un problema più esteso, disaccoppiando non solo rispetto ad un RDBMS, ma anche rispetto ad una directory LDAP, per esempio).

Il vero punto su cui concentrarsi, e che non esclude il mapping manuale o tecniche ibride, è il disaccoppiamento, appunto, tra storage e regole di business.

Il primo motivo del disaccoppiamento è quello di non far dipendere la struttura delle regole di business dalle particolarità dei singoli storage (es: differenze nella sintassi SQL, laddove non coperta dallo standard; passaggio da un tipo di storage - es: RDBMS - ad un altro - es: LDAP).
Il secondo motivo è la separazione delle competenze e delle responsabilità all'interno del team, per slegare quelle necessarie per l'object model da quelle necessarie per il DB - da qui in poi, quando parlerò di DB, si deve intendere che lo utilizzerò solo come esempio di storage.

Proviamo a tracciare i pro ed i contro di questo pattern:

PRO
- maggior semplicità e naturalezza dell'interfaccia applicativa (API)
- gli sviluppatori delle business rules non necessitano di avere competenza del DB, ma solo del linguaggio (Java o simili)
- gli internals del DB vengono incapsulati (così la struttura del DB e l'engine stesso possono variare, almeno entro certi limiti, indipendentemente dagli altri oggetti)

CONTRO
- la navigazione dei dati è preordinata (o carichi tutti i dati in un colpo solo, anche se non serviranno mai, o si deve preordinare una sequenza o una sorta di dipendenza - questi problemi sono solo parzialmente risolti da vari metodi di lazy loading)
- quindi: il carico sul DB può diventare imprevedibile, dal momento che chi usa il DAO può pensare, dato il contratto, di stare richiedendo un solo, innocuo, campo, mentre il DAO, per recuperarlo potrebbe dover attivare più query in cascata, laddove, magari, una o più delle query intermedie poteva essere inutile per lo scopo specifico
- una modifica alla struttura del DB quasi certamente implica una modifica al DAO e, non si può escludere che sia necessaria una modifica anche sul client.
- si potrebbe obiettare che queste problematiche non esistono o sono molto ridotte se si rende persistente l'intero oggetto come LOB o simili. A prescindere dal fatto che, così facendo, vi giocate la possibilità di fare query utili al vostro cliente, (che domani vi chiederà un nuovo report, incrociando i dati in un modo che su un relazionale sarebbe stato banale e sulle vostre classi e oggetti diventerà un inferno), il punto è che qualcuno, al DB, deve accedere, per esempio, per il primo caricamento dei dati nel DAO o per rendere persistente l'oggetto. Se questo qualcuno dovesse essere Hibernate, ci potremmo trovare nella spiacevole situazione di scoprire che le query che è in grado di eseguire sono solo quelle più standard e a noi servono quasi solo le altre, per questo progetto. Se chiederete aiuto ai vostri DBA, a questo punto, troverete gente che scrolla la testa e corruccia lo sguardo.
- ogni volta che aggiungete uno strato, oltre a nascondervi il reale funzionamento sottostante (nel caso di problemi, lo sapete che bisogna aprire la scatola, vero?), certamente non migliorate le performance, nonostante tutte le cache che vi possano venire in mente
- si riduce moltissimo la possibilità di sfruttare la potenza ed efficienza offerta dall'RDBMS nell'eseguire Stored Procedure
- nel caso i DAO siano usati diffusamente nell'applicazione, eventuali modifiche potrebbero non portare a regression test se l'applicazione è fatta bene, perchè è il contratto che mi garantisce che le modifiche siano invisibili, mentre è ovvio che una modifica alle interfacce potrebbe portare a molti problemi (che, però, dovrebbero essere individuati in fase di compilazione). Può essere buona norma lasciare intatte le vecchie interfacce, deprecarle ed impostarne di nuove, per i nuovi oggetti. Purtroppo, tutte queste precauzioni, spesso, si scontrano con una realtà per cui le applicazioni sono fatte bene, ma non benissimo (e quindi ci sono regression test da considerare) e per cui i metodi deprecati possono perdere così tanto significato da costringere a modificare comunque i client per referenziare i nuovi metodi.

TRADOTTO IN ALTRI TERMINI
- apparente maggior velocità di sviluppo e risorse con skill minori (ovvero, meno costose) in fase iniziale
- apparente disaccoppiamento tra DB e classi
si trasformano in:
- minor flessibilità
- maggiori costi di gestione e manutenzione

Che poi sarebbe la Law of Diminishing Returns di cui parla Ted Neward: In the case of automated Object/Relational Mapping, [...] that early successes yield a commitment to use O/R-M in places where success becomes more elusive, and over time, isn't a success at all due to the overhead of time and energy required to support it through all possible use-cases

Quali metodi ci sono per ridurre gli impatti? Sempre nell'articolo di cui sopra se ne citano 6 (!!) che mi sembrano parecchio esaustivi.
Cito solo il numero 6 (gli altri li avrete certamente già letti da soli) perchè è quello che preferisco e tendo ad usare maggiormente: Integrazione di concetti relazionali in frameworks.
Riassunto e parafrasato per riportarlo sul discorso DAO che abbiamo fatto fin qui, potrebbe suonare come: mantenete il design pattern DAO, ma appoggiandovi su strutture un po' più generiche, evitando di creare classi che rappresentino un concetto che, con quei metodi ed attributi, utilizzerete solo in quel particolare progetto.

Resta vero, peraltro, che ha (molto) senso mantenere l'utilizzo della persistenza (e di oggetti come Hibernate, di conseguenza), soprattutto laddove esistano strutture:
- gerarchiche già in partenza e quindi, con una naturale presenza di una navigazione preferenziale dei dati
- statiche o semistatiche
(che poi è come se vi stessi consigliando di considerare anche la soluzione numero 4, quella denominata Accettazione delle limitazioni dell'ORM)

Casomai non vi basti quanto finora detto e citato, vi rimando anche ad un altro articolo, molto pratico e poco teorico (al punto da evidenziare problemi che sono tipici di un solo ambiente e non dell'ORM in generale), particolarmente interessante laddove riassume i costi di un progetto basato su tali tecnologie e di cui vi suggerisco la lettura anche dei commenti.

Ci sarebbe ancora... no, per stavolta vi grazio :)

Bye

    Depa


Nessun commento: