Mondi su mondi, sistemi di sistemi.

Core Data, sì o no?

Wednesday, March 3rd, 2010

Premessa doverosa: non ho mai usato Core Data anche se ho usato in lungo e in largo l’EOF, fino a lasciarlo perdere. I post1 di Brent Simmons in cui dice di aver rinunciato a usare Core Data sull’iPhone per problemi di pre­stazioni, quindi, non poteva sfuggirmi, tanto più che ha generato diverse reazioni interes­santi2.

Quello che mi interessa discutere non è la scelta in sé, che è solo buon senso applicato alla programmazione, ovvero, lavora sempre al più alto livello di astrazione pos­sibile, scendi sotto solo se sei obbligato.

Quello che continuo a non capire è come si possa sostenere che una cosa come update newsItems set read = 1 where... stia a un livello di astrazione più basso di un loop in Objective-C o qualche altro linguaggio.

Non mi sfuggono gli aspetti problematici del fare interagire un programma con un database né che secondo alcuni Core Data non è un database o un ORM3, ma è proprio questo arguire intorno al cosa sia Core Data mi rende sospettoso perché queste discus­sioni sembrano spostare il problema nell’àmbito delle aspettative sbagliate.

Secondo la documentazione ufficiale:

The technically correct way to describe Core Data is as an object-graph management and persistence framework. In down-to-earth terms, this means that Core Data organizes the application’s model layer into a set of defined in-memory data objects. Core Data tracks changes to these objects and can reverse those changes on demand, such as when a user performs an undo command. Then, when it is time to save changes to your application’s data, Core Data takes care of archiving the objects to a persistent store. (…)

Core Data builds on some of the concepts of enterprise-class database application frameworks, such as the Enterprise Objects Framework in WebObjects. However, make no mistake, Core Data is not an object-relational database access framework. Instead, it uses concepts from the database world to take application data management to a new level.

Ora, se diamo per buona questa descrizione — e non vedo perché non dovremmo — bisogna chiedersi: le aspettative di Brent Simmons erano ragionevoli? Credo di sì, perché se “usi dei concetti dal mondo dei database” non è strano aspettarsi un modo efficiente di fare un update. Giusto?

OR Mappers: verso altre direzioni?

Friday, April 3rd, 2009

In uno dei post pre­cedenti accennavo al fatto che il giudizio sui domain model — e di conseguenza sugli ORM — tiene conto di criteri di ordine diverso: ci sono ragioni teoriche — di principio, direi — e ragioni pratiche.

Per quanto riguarda le ragioni teoriche credo di essermi già dilungato abbastanza, mentre sulle ragioni pratiche abbiamo cominciato a parlarne in relazione al caching. Oggi vorrei discuterne un’altra di tipo pratico. 

Uno dei modi di analizzare la struttura di un’applicazione è quella di ragionare per livelli.  Prendendo spunto da Fowler, abbiamo: un livello di pre­sentazione, uno di sorgente dei dati e uno con la cosiddetta domain logic. Grosso modo, pos­siamo dire che il primo livello corrisponde al browser, il secondo all’application server, il terzo al database.

Sorvoliamo su molti dettagli (ad es. se considerare il JavaScript che gira nel browser o la stored procedure nel database come facenti parte della domain logic o meno ecc.) e concentriamoci su quello che potremmo chiamare il tasso di mutazione nei vari livelli.

Sul lato del browser abbiamo la combinazione HTML+CSS+JavaScript da tempo immemore. Da qualche anno, con AJAX, la porzione di JavaScript è cresciuta a dismisura ed è cambiato il modo di considerare JavaScript come linguaggio; l’HTML è pas­sato attraverso varie revisioni e ramificazioni. Tuttavia, pos­siamo dire che nel complesso è cambiato relativamente poco.

All’estremo opposto abbiamo uno scenario simile. L’SQL, con i suoi limiti, le sue imperfezioni e i suoi diversi dialetti è certamente un qualcosa in continua evoluzione, ma senza scos­soni e in un quadro concettuale molto stabile. Altrettanto stabili sono le installazioni: una volta messo in piedi, è difficile che si passi ad un altro sistema.

Se guardiamo al livello intermedio troviamo invece una situazione completamente diversa. Abbiamo a disposizione tutti i linguaggi pos­sibili, con tutti gli approcci pos­sibili (ho perso il conto dei framework web in Java), che mutano a velocità sostenuta.

Non solo. L’avvento di AJAX ha spostato il baricentro verso il browser, sottraendo funzioni al livello intermedio, sino ad arrivare ad architetture come CouchDB, in cui questo livello non c’è proprio più.

E quindi mi sono fatto la domanda inevitabile: perché investirci ancora risorse? Ha ancora senso?

OR Mappers: il caching

Wednesday, April 1st, 2009

Fra i vantaggi che sono solitamente ascritti agli ORM c’è il caching. Si sfrutta la pos­sibilità di creare in memoria una rappresentazione persistente di righe nel database, sotto forma di un grafo di oggetti, per evitare di dover andare a ripescare dal database medesimo i dati ogni volta che serve.

È un’idea sensata, ovviamente, ma credo che abbia un uso più limitato di quanto si pensi, soprattutto in tempi come questi fatti di architetture distribuite, dove la cache può diventare un’arma a doppio taglio.

Ad esempio, quando seguivo con più attenzione la mailing list di WebObjects c’era spesso una domanda ricorrente “Ho n istanze della stessa applicazione, collegate allo stesso database; come faccio a ‘far vedere’ le modifiche dell’istanza A all’istanza B?”

Per risolvere questo problema nel tempo sono state sviluppate diverse soluzioni, l’ultima di cui sono a conoscenza è composta da er.extensions.remoteSynchronizer e er.jgroups (che usa, appunto, JGroups).

La comples­sità aumenta considerevolmente e abbiamo comunque risolto solo una parte del problema, visto che se un processo aggiorna il database al di fuori del contesto delle applicazioni A e B le loro cache risultano comunque invalidate.

Inoltre, il problema potrebbe essere considerato risolto solo se ci limitiamo a sistemi tradizionali client/server, ma su web cambia tutto: è un’architettura fondamentalmente stateless, dove le cache intermedie non vanno molto d’accordo con questa caratteristica (un’altra manifestazione dell’end-to-end argument).

Se aggiungiamo questo para­metro al calcolo dei costi/benefici ci accorgiamo che il vantaggio del caching risulta molto più ambiguo.

OR Mappers: l’importanza degli strumenti

Monday, March 30th, 2009

Qualche anno fa, Charles Petzold si è domandato se l’uso di Visual Studio possa rimbambirci:

Life without Visual Studio is unimaginable, and yet, no less than PowerPoint, Visual Studio causes us to do our jobs in various pre­defined ways, and I, for one, would be much happier if Visual Studio did much less than what it does. Certain features in Visual Studio are supposed to make us more productive, and yet for me, they seem to denigrate and degrade the programming experience. #

Cito questo paper per suggerire che gli strumenti che usiamo pos­sono avere veri e propri effetti collaterali. Bene, ma che c’entra con gli ORM?

C’entra in questo senso: se gli strumenti che uso non mi consentono di avere una visione d’insieme di tutti gli aspetti dell’applicazione, ho più difficoltà a ricostruire mentalmente questa visione. Più il progetto è complesso e più questo problema diventa sensibile. Se potessi usare un solo linguaggio la situazione sarebbe semplificata e gestibile completamente all’interno di un IDE.

Ecco quindi che suona come una buona idea quella di evitare l’uso dell’SQL attraverso una mappatura verso un qualche linguaggio a oggetti. È in fondo la stessa idea diGWT, applicata questa volta a JavaScript.

Ovviamente gli IDE si sono evoluti e quindi pos­sono gestire molto meglio di prima più linguaggi in modo integrato, ma quando è nato l’EOF tutto questo era ancora di là da venire: dove oggi si usa solo Eclipse una volta c’erano il Project Builder, il WebObjects Builder, l’EOModeler (e poi succes­sivamente anche il Rule Editor). Ma, appunto, era un limite degli strumenti, non una neces­sità intrinseca per poter programmare produttivamente con i database.

OR Mappers: pro e contro i domain models

Monday, March 23rd, 2009

Una obiezione che viene subito in mente contro l’idea di ridurre i domain objects a semplici EOGenericRecord è quello dell’antipattern (o code smell, fate voi) del cosidetto Anemic Domain Model:

The basic symptom of an Anemic Domain Model is that at first blush it looks like the real thing. There are objects, many named after the nouns in the domain space, and these objects are connected with the rich relationships and structure that true domain models have.

The catch comes when you look at the behavior, and you realize that there is hardly any behavior on these objects, making them little more than bags of getters and setters. Indeed often these models come with design rules that say that you are not to put any domain logic in the the domain objects. Instead there are a set of service objects which capture all the domain logic. These services live on top of the domain model and use the domain model for data.

Sembra una descrizione esatta di quello a cui porta il mio approccio. Ci sono però delle ulteriori considerazioni da fare.

Prima di tutto, l’EOGenericRecord non è proprio una semplice Data Class e fornisce già un bel po’ di logica pronta all’uso e se non basta c’è la versione pompata di Wonder. Di conseguenza il mio approccio sembra più a quello che viene definito un Service Layer:

Application Layer [his name for Service Layer]: Defines the jobs the software is supposed to do and directs the expres­sive domain objects to work out problems. The tasks this layer is responsible for are meaningful to the business or neces­sary for interaction with the application layers of other systems. This layer is kept thin. It does not contain business rules or knowledge, but only coordinates tasks and delegates work to collaborations of domain objects in the next layer down. It does not have state reflecting the business situation, but it can have state that reflects the progress of a task for the user or the program.

Domain Layer (or Model Layer): Responsible for representing concepts of the business, information about the business situation, and business rules. State that reflects the business situation is controlled and used here, even though the technical details of storing it are delegated to the infrastructure. This layer is the heart of business software.

In secondo luogo non mi sembra che le argomentazioni di Fowler siano molto convincenti. È vero che l’accoppiamento fra dati e procedure è la ragion d’essere dell’OOP ma questo non dimostra che sia sempre l’approccio migliore (cosa che anche Fowler riconosce). Inoltre non viene portato un singolo esempio concreto di una situazione in cui un Domani Model è chiaramente superiore, a parte gli esempi ovvi.

Distinguere chiaramente i pro e i contro è difficile anche perché i criteri di valutazione sono in parte teorici e in parte pratici (almeno nel mio caso). Ci torneremo.

OR Mappers: business logic e operazioni sul db

Friday, March 20th, 2009

Questa serie di post sta diventando una saga… Come spesso capita, una volta che si inizia a dubitare, le domande fioccano. 

Di solito, una volta mappata la tabella sulla classe poi pos­siamo implementare la cosiddetta business logic in quella classe. Spesso, però, succede che questa business logic tocchi più classi contemporaneamente; quindi mi sono chiesto: non sarebbe più semplice considerare quelle classi come “pas­sive” — per così dire — e implementarla completamente al di fuori di esse? Nell’EOF, questo equivale a mappare tutte le tabelle con l’EOGenericRecord o una sua sottoclasse.

Inciso: questo è un difetto più generale dei linguaggi OO: devi sempre avere una classe o un istanza di quella classe a cui “attaccare” un metodo. So benis­simo che solitamente questa caratteristica non è vista in modo negativo ma sono arrivato a un’altra conclusione; ne parleremo un’altra volta.

Sull’altro versante, quello del database, abbiamo tutte quelle operazioni come le join e gli aggregati (SUM, AVG e così via) che sono molto più facilmente utilizzabili direttamente a quel livello. In questi casi le uniche pos­sibilità sono: aggirare la mappatura e manipolare i risultati grezzi o definire delle ulteriori mappature.

Di solito pre­ferisco usare la prima soluzione: la mappatura non sarà completa ma copre almeno i casi principali. La seconda appesantisce il domain model di entità/classi con uno scopo limitato a casi speciali.

OR Mappers: i primi dubbi

Wednesday, March 18th, 2009

A dispetto dei “great blunders” (1, 2), per un certo tempo ho tenuto la vocina critica opportunamente al di sotto della soglia uditiva; mi sembravano questioni marginali e comunque lavorare con l’EOF era divertente ed elegante, soprattutto rispetto a prodotti analoghi, come Hibernate.

Infatti, ho cominciato ad avere qualche dubbio serio su questioni che non riguardano direttamente la scorretta analogia classe-tabella. Semplicemente, un giorno ho cominciato a chiedermi se, al posto di mappare il nome del cliente da VARCHAR a String, non sarebbe stato meglio definire una classe ad hoc.

In fondo — mi dicevo — che senso ha scrivere una cosa del tipo cliente.nome().equals(indirizzo.via()), visto il loro significato è completamente diverso? È vero, di solito usiamo String per entrambi i valori e anche gli eventuali vincoli d’integrità potrebbero essere molto simili, ma non ha molto senso confrontare un nome con una via.

Purtroppo, per quanto l’idea sia corretta, la comples­sità del codice schizza alle stelle perché non è semplice implementare la cosiddetta “specialization by constraint”; con l’EOF, in Java, non sono riuscito a ottenere risultati decenti.

E così ho cominciato a ripensare il mio approccio.

OR Mappers: the second great blunder

Tuesday, March 17th, 2009

Il secondo problema nella supposta equivalenza fra classi di oggetti e tabelle può essere logicamente collegato al primo ma può anche essere pre­sente per conto proprio. Questo secondo errore consiste nell’esporre i puntatori e le tabelle.

Il nocciolo del problema è che,  per definizione, i puntatori sono variabili, non valori e il modello relazionale non consente l’uso di variabili di tupla o di attributo.

Credo sia un errore minore rispetto al primo e soprattutto non molto rilevante nella discus­sione sugli ORM. Infatti, nel momento stesso in cui creo un mapping, mi muovo all’interno di un certo ambiente di programmazione che, di nuovo per definizione, non è relazionale.

OR Mappers

Thursday, March 12th, 2009

Ascoltando questo podcast mi è venuto un attacco di nostalgia pensando a quanto tempo ho pas­sato a usare l’EOF, a quanto ne ho tes­suto le lodi e a quanto poco ne senta adesso la mancanza, avendo cambiato radicalmente idea.

Oggi penso che gli ORM siano quasi sempre la soluzione sbagliata, se non per l’indipendenza dai vari dialetti SQL.

Il problema sta nella supposta equivalenza fra classe e tabella. La tabella CLIENTE diventa la classe Cliente; le colonne diventano diventano attributi/ivars/properties/metodi della classe; ogni riga della tabella diventa un’istanza della classe. Semplice, no?

In effetti la tentazione è forte, ma credo sia un’idea sbagliata e la ragione è quella definita da Date come “The first great blunder”. Nota sul link: l’ho messo per comodità ma raccomando di leggere il suo libro “An introduction to database systems”.

Per molto tempo ho considerato le obiezioni di Date troppo formali, quasi capziose, ma mi sbagliavo. Credo che l’integrità concettuale così puntigliosamente difesa sia una cosa importante e che senza di essa le pre­messe per un buon design del software siano minate alle fondamenta.

Chiavi Composite

Sunday, February 4th, 2007

Questo post è il clas­sico esempio dell’eterna diatriba su quale sia il miglior tipo di chiave da usare in un database, in questo caso con l’aggiunta della variante sulle chiavi composite e dalla prospettiva dell’applicazione.

A mio parere il problema del post – non ho letto i commenti – è che semplifica troppo la questione, non tenendo conto dei difetti della soluzione con chiavi sintetiche (o surrogate).

Il punto da cui partire è: cosa significano le chiavi composite? Beh, ci danno un’informazione riguardo ai vincoli d’integrità del database. Ad es. date due tabelle, “Utenti” e “Gruppi” e una tabella di join, la chiave composita sulla tabella di join, formata dalle chiavi primarie di “Gruppi” e “Utenti” ci dice un utente non può – ovviamente – appartenere due volte allo stesso gruppo. Anche se usas­simo una chiave primaria surrogata, questo vincolo deve rimanere comunque e per farlo rispettare ci sarà pre­sumibilmente bisogno di un ulteriore indice sulle due foreign keys.

Insomma, se è vero che le chiavi composte sono meno “maneggevoli”, bisogna anche ricordare qual’è il prezzo da pagare per le soluzioni alternative. Aggiungo anche che framework come l’EOF rendono molto semplice lavorare con queste configurazioni.

C’è un solo caso, comunque piuttosto frequente in design normalizzati, in cui userei una chiave sintetica e si pre­senta nel momento in cui ci sono delle foreign key che puntano alla chiave primaria composta. In questa situazione usare tutti i componenti della chiave primaria per creare il vincolo d’integrità diventa rapidamente impraticabile. Dev’essere però chiaro che si tratta di una neces­sità pratica e non di modellazione.