Mondi su mondi, sistemi di sistemi.

Core Data, sì o no?

Wednesday, March 3rd, 2010

Premessa dove­rosa: non ho mai usato Core Data anche se ho usato in lungo e in largo l’EOF, fino a lasciarlo per­dere. I post1 di Brent Simmons in cui dice di aver rinun­ciato a usare Core Data sull’iPhone per pro­blemi di pre­sta­zioni, quindi, non poteva sfug­girmi, tanto più che ha gene­rato diverse rea­zioni inte­res­santi2.

Quello che mi inte­ressa discu­tere non è la scelta in sé, che è solo buon senso appli­cato alla pro­gram­ma­zione, ovvero, lavora sem­pre al più alto livello di astra­zione pos­si­bile, scendi sotto solo se sei obbligato.

Quello che con­ti­nuo a non capire è come si possa soste­nere che una cosa come update newsItems set read = 1 where... stia a un livello di astra­zione più basso di un loop in Objective-C o qual­che altro linguaggio.

Non mi sfug­gono gli aspetti pro­ble­ma­tici del fare inte­ra­gire un pro­gramma con un data­base né che secondo alcuni Core Data non è un data­base o un ORM3, ma è pro­prio que­sto arguire intorno al cosa sia Core Data mi rende sospet­toso per­ché que­ste discus­sioni sem­brano spo­stare il pro­blema nell’àmbito delle aspet­ta­tive sbagliate.

Secondo la docu­men­ta­zione ufficiale:

The tech­ni­cally cor­rect way to describe Core Data is as an object-graph mana­ge­ment and per­si­stence fra­mework. In down-to-earth terms, this means that Core Data orga­ni­zes the application’s model layer into a set of defi­ned in-memory data objects. Core Data tracks chan­ges to these objects and can reverse those chan­ges on demand, such as when a user per­forms an undo com­mand. Then, when it is time to save chan­ges to your application’s data, Core Data takes care of archi­ving the objects to a per­si­stent store. (…)

Core Data builds on some of the con­cepts of enterprise-class data­base appli­ca­tion fra­meworks, such as the Enterprise Objects Framework in WebObjects. However, make no mistake, Core Data is not an object-relational data­base access fra­mework. Instead, it uses con­cepts from the data­base world to take appli­ca­tion data mana­ge­ment to a new level.

Ora, se diamo per buona que­sta descri­zione — e non vedo per­ché non dovremmo — biso­gna chie­dersi: le aspet­ta­tive di Brent Simmons erano ragio­ne­voli? Credo di sì, per­ché se “usi dei con­cetti dal mondo dei data­base” non è strano aspet­tarsi un modo effi­ciente di fare un update. Giusto?

OR Mappers: verso altre direzioni?

Friday, April 3rd, 2009

In uno dei post pre­ce­denti accen­navo al fatto che il giu­di­zio sui domain model — e di con­se­guenza sugli ORM — tiene conto di cri­teri di ordine diverso: ci sono ragioni teo­ri­che — di prin­ci­pio, direi — e ragioni pratiche.

Per quanto riguarda le ragioni teo­ri­che credo di essermi già dilun­gato abba­stanza, men­tre sulle ragioni pra­ti­che abbiamo comin­ciato a par­larne in rela­zione al caching. Oggi vor­rei discu­terne un’altra di tipo pratico. 

Uno dei modi di ana­liz­zare la strut­tura di un’applicazione è quella di ragio­nare per livelli.  Prendendo spunto da Fowler, abbiamo: un livello di pre­sen­ta­zione, uno di sor­gente dei dati e uno con la cosid­detta domain logic. Grosso modo, pos­siamo dire che il primo livello cor­ri­sponde al bro­w­ser, il secondo all’application ser­ver, il terzo al database.

Sorvoliamo su molti det­ta­gli (ad es. se con­si­de­rare il JavaScript che gira nel bro­w­ser o la sto­red pro­ce­dure nel data­base come facenti parte della domain logic o meno ecc.) e con­cen­tria­moci su quello che potremmo chia­mare il tasso di muta­zione nei vari livelli.

Sul lato del bro­w­ser abbiamo la com­bi­na­zione HTML+CSS+JavaScript da tempo imme­more. Da qual­che anno, con AJAX, la por­zione di JavaScript è cre­sciuta a dismi­sura ed è cam­biato il modo di con­si­de­rare JavaScript come lin­guag­gio; l’HTML è pas­sato attra­verso varie revi­sioni e rami­fi­ca­zioni. Tuttavia, pos­siamo dire che nel com­plesso è cam­biato rela­ti­va­mente poco.

All’estremo oppo­sto abbiamo uno sce­na­rio simile. L’SQL, con i suoi limiti, le sue imper­fe­zioni e i suoi diversi dia­letti è cer­ta­mente un qual­cosa in con­ti­nua evo­lu­zione, ma senza scos­soni e in un qua­dro con­cet­tuale molto sta­bile. Altrettanto sta­bili sono le instal­la­zioni: una volta messo in piedi, è dif­fi­cile che si passi ad un altro sistema.

Se guar­diamo al livello inter­me­dio tro­viamo invece una situa­zione com­ple­ta­mente diversa. Abbiamo a dispo­si­zione tutti i lin­guaggi pos­si­bili, con tutti gli approcci pos­si­bili (ho perso il conto dei fra­mework web in Java), che mutano a velo­cità sostenuta.

Non solo. L’avvento di AJAX ha spo­stato il bari­cen­tro verso il bro­w­ser, sot­traendo fun­zioni al livello inter­me­dio, sino ad arri­vare ad archi­tet­ture come CouchDB, in cui que­sto livello non c’è pro­prio più.

E quindi mi sono fatto la domanda ine­vi­ta­bile: per­ché inve­stirci ancora risorse? Ha ancora senso?

OR Mappers: il caching

Wednesday, April 1st, 2009

Fra i van­taggi che sono soli­ta­mente ascritti agli ORM c’è il caching. Si sfrutta la pos­si­bi­lità di creare in memo­ria una rap­pre­sen­ta­zione per­si­stente di righe nel data­base, sotto forma di un grafo di oggetti, per evi­tare di dover andare a ripe­scare dal data­base mede­simo i dati ogni volta che serve.

È un’idea sen­sata, ovvia­mente, ma credo che abbia un uso più limi­tato di quanto si pensi, soprat­tutto in tempi come que­sti fatti di archi­tet­ture distri­buite, dove la cache può diven­tare un’arma a dop­pio taglio.

Ad esem­pio, quando seguivo con più atten­zione la mai­ling list di WebObjects c’era spesso una domanda ricor­rente “Ho n istanze della stessa appli­ca­zione, col­le­gate allo stesso data­base; come fac­cio a ‘far vedere’ le modi­fi­che dell’istanza A all’istanza B?”

Per risol­vere que­sto pro­blema nel tempo sono state svi­lup­pate diverse solu­zioni, l’ultima di cui sono a cono­scenza è com­po­sta da er.extensions.remoteSynchronizer e er.jgroups (che usa, appunto, JGroups).

La com­ples­sità aumenta con­si­de­re­vol­mente e abbiamo comun­que risolto solo una parte del pro­blema, visto che se un pro­cesso aggiorna il data­base al di fuori del con­te­sto delle appli­ca­zioni A e B le loro cache risul­tano comun­que invalidate.

Inoltre, il pro­blema potrebbe essere con­si­de­rato risolto solo se ci limi­tiamo a sistemi tra­di­zio­nali client/server, ma su web cam­bia tutto: è un’architettura fon­da­men­tal­mente sta­te­less, dove le cache inter­me­die non vanno molto d’accordo con que­sta carat­te­ri­stica (un’altra mani­fe­sta­zione dell’end-to-end argu­ment).

Se aggiun­giamo que­sto para­me­tro al cal­colo dei costi/benefici ci accor­giamo che il van­tag­gio del caching risulta molto più ambiguo.

OR Mappers: l’importanza degli strumenti

Monday, March 30th, 2009

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

Life without Visual Studio is uni­ma­gi­na­ble, and yet, no less than PowerPoint, Visual Studio cau­ses us to do our jobs in various pre­de­fi­ned ways, and I, for one, would be much hap­pier if Visual Studio did much less than what it does. Certain fea­tu­res in Visual Studio are sup­po­sed to make us more pro­duc­tive, and yet for me, they seem to deni­grate and degrade the pro­gram­ming expe­rience. #

Cito que­sto paper per sug­ge­rire che gli stru­menti che usiamo pos­sono avere veri e pro­pri effetti col­la­te­rali. Bene, ma che c’entra con gli ORM?

C’entra in que­sto senso: se gli stru­menti che uso non mi con­sen­tono di avere una visione d’insieme di tutti gli aspetti dell’applicazione, ho più dif­fi­coltà a rico­struire men­tal­mente que­sta visione. Più il pro­getto è com­plesso e più que­sto pro­blema diventa sen­si­bile. Se potessi usare un solo lin­guag­gio la situa­zione sarebbe sem­pli­fi­cata e gesti­bile com­ple­ta­mente all’interno di un IDE.

Ecco quindi che suona come una buona idea quella di evi­tare l’uso dell’SQL attra­verso una map­pa­tura verso un qual­che lin­guag­gio a oggetti. È in fondo la stessa idea diGWT, appli­cata que­sta volta a JavaScript.

Ovviamente gli IDE si sono evo­luti e quindi pos­sono gestire molto meglio di prima più lin­guaggi in modo inte­grato, ma quando è nato l’EOF tutto que­sto 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 suc­ces­si­va­mente anche il Rule Editor). Ma, appunto, era un limite degli stru­menti, non una neces­sità intrin­seca per poter pro­gram­mare pro­dut­ti­va­mente con i database.

OR Mappers: pro e contro i domain models

Monday, March 23rd, 2009

Una obie­zione che viene subito in mente con­tro l’idea di ridurre i domain objects a sem­plici EOGenericRecord è quello dell’antipattern (o code smell, fate voi) del cosi­detto Anemic Domain Model:

The basic symp­tom 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 con­nec­ted with the rich rela­tion­ships and struc­ture that true domain models have.

The catch comes when you look at the beha­vior, and you rea­lize that there is hardly any beha­vior on these objects, making them lit­tle more than bags of get­ters and set­ters. 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 ser­vice objects which cap­ture all the domain logic. These ser­vi­ces live on top of the domain model and use the domain model for data.

Sembra una descri­zione esatta di quello a cui porta il mio approc­cio. Ci sono però delle ulte­riori con­si­de­ra­zioni da fare.

Prima di tutto, l’EOGenericRecord non è pro­prio una sem­plice Data Class e for­ni­sce già un bel po’ di logica pronta all’uso e se non basta c’è la ver­sione pom­pata di Wonder. Di con­se­guenza il mio approc­cio sem­bra più a quello che viene defi­nito un Service Layer:

Application Layer [his name for Service Layer]: Defines the jobs the soft­ware is sup­po­sed to do and directs the expres­sive domain objects to work out pro­blems. The tasks this layer is respon­si­ble for are mea­ning­ful to the busi­ness or neces­sary for inte­rac­tion with the appli­ca­tion layers of other systems. This layer is kept thin. It does not con­tain busi­ness rules or kno­w­ledge, but only coor­di­na­tes tasks and dele­ga­tes work to col­la­bo­ra­tions of domain objects in the next layer down. It does not have state reflec­ting the busi­ness situa­tion, but it can have state that reflects the pro­gress of a task for the user or the program.

Domain Layer (or Model Layer): Responsible for repre­sen­ting con­cepts of the busi­ness, infor­ma­tion about the busi­ness situa­tion, and busi­ness rules. State that reflects the busi­ness situa­tion is con­trol­led and used here, even though the tech­ni­cal details of sto­ring it are dele­ga­ted to the infra­struc­ture. This layer is the heart of busi­ness software.

In secondo luogo non mi sem­bra che le argo­men­ta­zioni di Fowler siano molto con­vin­centi. È vero che l’accoppiamento fra dati e pro­ce­dure è la ragion d’essere dell’OOP ma que­sto non dimo­stra che sia sem­pre l’approccio migliore (cosa che anche Fowler rico­no­sce). Inoltre non viene por­tato un sin­golo esem­pio con­creto di una situa­zione in cui un Domani Model è chia­ra­mente supe­riore, a parte gli esempi ovvi.

Distinguere chia­ra­mente i pro e i con­tro è dif­fi­cile anche per­ché i cri­teri di valu­ta­zione sono in parte teo­rici e in parte pra­tici (almeno nel mio caso). Ci torneremo.

OR Mappers: business logic e operazioni sul db

Friday, March 20th, 2009

Questa serie di post sta diven­tando una saga… Come spesso capita, una volta che si ini­zia a dubi­tare, le domande fioccano. 

Di solito, una volta map­pata la tabella sulla classe poi pos­siamo imple­men­tare la cosid­detta busi­ness logic in quella classe. Spesso, però, suc­cede che que­sta busi­ness logic toc­chi più classi con­tem­po­ra­nea­mente; quindi mi sono chie­sto: non sarebbe più sem­plice con­si­de­rare quelle classi come “pas­sive” — per così dire — e imple­men­tarla com­ple­ta­mente al di fuori di esse? Nell’EOF, que­sto equi­vale a map­pare tutte le tabelle con l’EOGenericRecord o una sua sottoclasse.

Inciso: que­sto è un difetto più gene­rale dei lin­guaggi OO: devi sem­pre avere una classe o un istanza di quella classe a cui “attac­care” un metodo. So benis­simo che soli­ta­mente que­sta carat­te­ri­stica non è vista in modo nega­tivo ma sono arri­vato a un’altra con­clu­sione; ne par­le­remo un’altra volta.

Sull’altro ver­sante, quello del data­base, abbiamo tutte quelle ope­ra­zioni come le join e gli aggre­gati (SUM, AVG e così via) che sono molto più facil­mente uti­liz­za­bili diret­ta­mente a quel livello. In que­sti casi le uni­che pos­si­bi­lità sono: aggi­rare la map­pa­tura e mani­po­lare i risul­tati grezzi o defi­nire delle ulte­riori mappature.

Di solito pre­fe­ri­sco usare la prima solu­zione: la map­pa­tura non sarà com­pleta ma copre almeno i casi prin­ci­pali. La seconda appe­san­ti­sce il domain model di entità/classi con uno scopo limi­tato a casi speciali.

OR Mappers: i primi dubbi

Wednesday, March 18th, 2009

A dispetto dei “great blun­ders” (1, 2), per un certo tempo ho tenuto la vocina cri­tica oppor­tu­na­mente al di sotto della soglia udi­tiva; mi sem­bra­vano que­stioni mar­gi­nali e comun­que lavo­rare con l’EOF era diver­tente ed ele­gante, soprat­tutto rispetto a pro­dotti ana­lo­ghi, come Hibernate.

Infatti, ho comin­ciato ad avere qual­che dub­bio serio su que­stioni che non riguar­dano diret­ta­mente la scor­retta ana­lo­gia classe-tabella. Semplicemente, un giorno ho comin­ciato a chie­dermi se, al posto di map­pare il nome del cliente da VARCHAR a String, non sarebbe stato meglio defi­nire una classe ad hoc.

In fondo — mi dicevo — che senso ha scri­vere una cosa del tipo cliente.nome().equals(indirizzo.via()), visto il loro signi­fi­cato è com­ple­ta­mente diverso? È vero, di solito usiamo String per entrambi i valori e anche gli even­tuali vin­coli d’integrità potreb­bero essere molto simili, ma non ha molto senso con­fron­tare un nome con una via.

Purtroppo, per quanto l’idea sia cor­retta, la com­ples­sità del codice schizza alle stelle per­ché non è sem­plice imple­men­tare la cosid­detta “spe­cia­li­za­tion by con­straint”; con l’EOF, in Java, non sono riu­scito a otte­nere risul­tati decenti.

E così ho comin­ciato a ripen­sare il mio approccio.

OR Mappers: the second great blunder

Tuesday, March 17th, 2009

Il secondo pro­blema nella sup­po­sta equi­va­lenza fra classi di oggetti e tabelle può essere logi­ca­mente col­le­gato al primo ma può anche essere pre­sente per conto pro­prio. Questo secondo errore con­si­ste nell’esporre i pun­ta­tori e le tabelle.

Il noc­ciolo del pro­blema è che,  per defi­ni­zione, i pun­ta­tori sono varia­bili, non valori e il modello rela­zio­nale non con­sente l’uso di varia­bili di tupla o di attributo.

Credo sia un errore minore rispetto al primo e soprat­tutto non molto rile­vante nella discus­sione sugli ORM. Infatti, nel momento stesso in cui creo un map­ping, mi muovo all’interno di un certo ambiente di pro­gram­ma­zione che, di nuovo per defi­ni­zione, non è relazionale.

OR Mappers

Thursday, March 12th, 2009

Ascoltando que­sto pod­cast mi è venuto un attacco di nostal­gia pen­sando 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 man­canza, avendo cam­biato radi­cal­mente idea.

Oggi penso che gli ORM siano quasi sem­pre la solu­zione sba­gliata, se non per l’indipendenza dai vari dia­letti SQL.

Il pro­blema sta nella sup­po­sta equi­va­lenza fra classe e tabella. La tabella CLIENTE diventa la classe Cliente; le colonne diven­tano diven­tano attributi/ivars/properties/metodi della classe; ogni riga della tabella diventa un’istanza della classe. Semplice, no?

In effetti la ten­ta­zione è forte, ma credo sia un’idea sba­gliata e la ragione è quella defi­nita da Date come “The first great blun­der”. Nota sul link: l’ho messo per como­dità ma rac­co­mando di leg­gere il suo libro “An intro­duc­tion to data­base systems”.

Per molto tempo ho con­si­de­rato le obie­zioni di Date troppo for­mali, quasi cap­ziose, ma mi sba­gliavo. Credo che l’inte­grità con­cet­tuale così pun­ti­glio­sa­mente difesa sia una cosa impor­tante e che senza di essa le pre­messe per un buon design del soft­ware siano minate alle fondamenta.

Chiavi Composite

Sunday, February 4th, 2007

Questo post è il clas­sico esem­pio dell’eterna dia­triba su quale sia il miglior tipo di chiave da usare in un data­base, in que­sto caso con l’aggiunta della variante sulle chiavi com­po­site e dalla pro­spet­tiva dell’applicazione.

A mio parere il pro­blema del post – non ho letto i com­menti – è che sem­pli­fica troppo la que­stione, non tenendo conto dei difetti della solu­zione con chiavi sin­te­ti­che (o surrogate).

Il punto da cui par­tire è: cosa signi­fi­cano le chiavi com­po­site? Beh, ci danno un’informazione riguardo ai vin­coli d’integrità del data­base. Ad es. date due tabelle, “Utenti” e “Gruppi” e una tabella di join, la chiave com­po­sita sulla tabella di join, for­mata dalle chiavi pri­ma­rie di “Gruppi” e “Utenti” ci dice un utente non può – ovvia­mente – appar­te­nere due volte allo stesso gruppo. Anche se usas­simo una chiave pri­ma­ria sur­ro­gata, que­sto vin­colo deve rima­nere comun­que e per farlo rispet­tare ci sarà pre­su­mi­bil­mente biso­gno di un ulte­riore indice sulle due foreign keys.

Insomma, se è vero che le chiavi com­po­ste sono meno “maneg­ge­voli”, biso­gna anche ricor­dare qual’è il prezzo da pagare per le solu­zioni alter­na­tive. Aggiungo anche che fra­mework come l’EOF ren­dono molto sem­plice lavo­rare con que­ste configurazioni.

C’è un solo caso, comun­que piut­to­sto fre­quente in design nor­ma­liz­zati, in cui use­rei una chiave sin­te­tica e si pre­senta nel momento in cui ci sono delle foreign key che pun­tano alla chiave pri­ma­ria com­po­sta. In que­sta situa­zione usare tutti i com­po­nenti della chiave pri­ma­ria per creare il vin­colo d’integrità diventa rapi­da­mente impra­ti­ca­bile. Dev’essere però chiaro che si tratta di una neces­sità pra­tica e non di modellazione.