giovedì 6 dicembre 2007

Metodologia OO2R

Ecco un modello pratico per la realizzazione di infrastrutture di persistenza per applicativi modellati su architetture Object Oriented. Il prodotto di questo modello non e' un vero e proprio DB Object Oriented, ma consente di sviluppare strati di persistenza consistenti per sistemi OO, sfruttando nel contempo quelli che sono i punti di forza (prestazionali) dei database relazionali.



Caratteristiche architetturali

Nello strato di persistenza non vengono implementati i metodi delle classi, ma soltanto gli attributi. Non sara' quindi possibile effettuare query con chiamate a metodi direttamente in SQL, ma sara' necessario passare dallo strato del linguaggio OO per questo tipo di query, con una penalizzazione delle prestazioni. Occorre quindi porre attenzione all'analisi architetturale del sistema, tenendo conto del trade-off tra design e performance.

Poiche' il sistema e' suddiviso in due strati (lo strato Java e lo strato DB) e' necessario sempre tenere presente quando e in che modo in due strati sono coinvolti nelle varie operazioni, per poter offrire una buona stima delle prestazioni del sistema in progetti reali dove queste siano essenziali.

Cerchero' sempre di sottolineare l'esatto livello di coinvolgimento dei due strati, con una digressione sulle performance.



Prerogative dei database OO

Elenco qui le golden rules dei database orientati agli oggetti: per quanto possibile cercherò di sviluppare un sistema che ricalchi queste regole, e quando non sarà possibile rispettarle, indicherò le motivazioni.

  • Oggetti complessi

Deve essere possibile creare oggetti complessi aggregando oggetti più semplici; inoltre deve essere possibile applicare i metodi di aggregazione ortogonalmente . Questa feature verrà implementata tramite una relazione di aggregazione.

  • Object identity

A differenza del modello relazionale in cui l'identità di una entità è relativa al valore dei suoi campi chiave, in un db object oriented è necessario identificare un oggetto indipendentemente dal suo valore; questa feature verrà implementa semplicemente inserendo come chiave di ogni tavola che rappresenta un oggetto un campo objectId (che in MySQL sarà un Integer con Auto-increment)

  • Incapsulazione

In questo caso, poichè i metodi degli oggetti non vengono implementati dal DB, ma dallo strato Java sovrastante, l'incapsulazione è implementata dall'oggetto “Java side”

  • Tipi e classi

Anche in questo caso ci baseremo sulle feature di Java. Solo lo strato di persistenza verrà delegato al DB, attraverso tavole rappresentanti le entità del sistema

  • Gerarchia di classi

Naturalmente la “Java side” del sistema implementa nativamente le gerarchie di classi. Da un punto di vista dello strato di persistenza, la relazione di ereditarietà verrà implementata utilizzando come chiave primaria e objectId della classe figlia, una foreign key puntante all'objectId della classe padre. Le interfaccie (che sono prive di attributi e quindi prive di stato di persistenza) verranno implementate come tavole il cui unico attributo è l'objectId. Questo per permettere il polimorfismo nelle query.



Componenti del sistema

Livello di database

Il database prevede due metaclassi di componenti:

  • Entità

  • Relazioni

Da un punto di vista OO le relazioni rimangono invariate rispetto al modello relazionale, mentre cambiano le entità: inoltre nasce un tipo di relazione di ereditarietà, che va quindi correttamente codificata in SQL.

Livello del linguaggio (Java)

Per ogni classe di oggetti persistenti sono necessari due componenti:

  • La classe stessa (che implementa DataObject)

  • Un gestore della persistenza specifico



La metodologia

Il punto di partenza di questa metodologia è il class diagram del sistema, che -ATTENZIONE! - dovrà essere nella “versione per l'implementazione in Java” piuttosto che nella “versione dell'architettura implementabile in qualunque linguaggio OO”. Questo perchè in Java tutti gli oggetti derivano da Object. Se questa feature viene in qualche modo sfruttata, anche nello strato di persistenza ci dovrà essere una tabella “Object” che offre foreign key a tutte le entità del sistema! Naturalmente questa implementazione potrebbe non essere accettabile (potrei avere un numero eccessivo di oggetti nel DB, tale da impattare troppo sulle prestazioni e sullo spazio utilizzato su disco). Inoltre lo strato di persistenza deve includere anche gli attributi privati e protetti dell'oggetto da serializzare (che normalmente nei diagrammi dell'architetto non compaiono).

Ad ogni classe persistente corrisponde una tabella di tipo Entità nel database. Inoltre ogni entità ha un objectId (regola della Object Identity dei DB ad oggetti) che è anche la chiave primaria della tavola.

Qualunque tipo di relazione (ereditarietà [is-a], composizione [has-a], oppure user defined [abita-presso], [ha-il-permesso-per], ecc) viene rappresentata da una relazione – sempre una tavola del DB – con eventuali attributi.

La relazione di ereditarietà viene espressa tramite una chiave esterna utilizzata anche come primary key nella classe specializzata.



Esempio pratico

Prendiamo un diagramma UML:


Per il momento tralasciamo i particolari implementativi, ci interessa principalmente la struttura.

Un DocumentoVendita è una classe astratta, implementata da Fattura e Bolla, che ne implementano il metodo astratto stampa().

Inoltre aggiungono ognuno un attributi specifico di questa classe. D'altra parte abbiamo un'interfaccia RigaDocumento, e due classi implementanti, RigaFattura e RigaBolla, ognuna con le sue caratteristiche specifiche (che tralasciamo). Un DocumentoVendita contiene (usa) una collezione di RigaDocumento (la relazione potrebbe essere has-many).

Vediamo come possiamo implementare in Java questo modello, e come possiamo realizzarne lo strato di persistenza su un DB relazionale:


La classe astratta DocumentoVendita viene implementata dalla tavola documento_vendita, che oltre ad enumerare tutti gli attributi (attenzione! sia pubblici che privati!) della classe aggiunge come PK objectId, e come attributo normale objectRTTI. Il primo è un numerico con auto-incremento, mentre il secondo contiene il nome della classe reale (foglia nell'albero della gerarchia) dell'oggetto polimorfo. Questo secondo attributo è necessario soltanto nelle classi base. Lo strato di persistenza delle classi che ereditano da documentoVendita, viene implementato attraverso tavole (una per ogni classe implementante, quindi bolla e fattura). Queste tavole contengono gli attributi specifici delle classi ereditate, oltre a objectId. Quest'ultimo però, oltre ad essere chiave primaria, è anche foreign key sull'omonimo campo della tavola della classe base (documento_vendita), e non è auto-increment. Di fatto lo stato persistente di un oggetto fattura è dato dal join naturale tra “bolla JOIN documento_vendita ON (bolla.objectId = documento_vendita.objectId)”.

[qui manca una bella disquisizione sulla relazione has_many]

Ora supponiamo di dover estendere il nostro sistema, aggiungendo un ulteriore livello di specializzazione nelle fatture:

In particolare abbiamo aggiunto la classe derivata FatturaPersonalizzata.

Vediamo come adattare lo schema del DB:

La query per il data retriving di una fattura personalizzata:

SELECT *

FROM fattura_personalizzata JOIN fattura JOIN documento_vendita

ON (fattura_personalizzata.objectId = fattura.objectId AND fattura.objectId = documento_vendita.objectId)

WHERE fattura_personalizzata.objectId = ?

E' importante notare che anche con una elevata profondità nel grafo della gerarchia, le ricerche per objectId hanno sempre un costo estremamente limitato!



New features for new OSes (part 3)

Advanced profiling for user and system configuration

Configuration should be classified in three fields: system configuration, users configuration and application configuration. These three sets can eventually intersect and share some data (for example, an application could check system configuration for printing or current user configuration for colour preferences), so the configuration system should be able to permit partial access to some data to some applications. This means that the configuration system should be much more intelligent than – for instance – a registry file, and some database capabilities will definitely come up in this context.

In a network environment, an advance configuration system should also provide the capacity to centralize system administration (in collaboration with the auto-update and auto-heal system) and to remote user configuration and permissions, so that the machines attached to the network are able to download on demand whatever the logged user needs.

How would be possible to obtain this behaviours? With a DBMS managed configuration system, we could identify three entities (systems, users and applications): each entity has its own credentials (in commercial systems this could also include licence checking) and it is stored in the configuration database. The database engine is able to retrieve informations also from the centralized server to obtain network administration and user distribution abilities; there are very strict rules on how entities can access data: for example an application executed from a certain user can only gain access to this specific user configuration, and is permitted to write configuration informations concerning only the “this user, this application” join; the very same limitations are applied in the “this application, this system” join.


lunedì 3 dicembre 2007

Aforisma

Sono convinto che l'informatica abbia molto in comune con la fisica. Entrambe si occupano di come funziona il mondo a un livello abbastanza fondamentale. La differenza, naturalmente, è che mentre in fisica devi capire come è fatto il mondo, in informatica sei tu a crearlo. Dentro i confini del computer, sei tu il creatore. Controlli - almeno potenzialmente - tutto ciò che vi succede. Se sei abbastanza bravo, puoi essere un dio. Su piccola scala. (Linus Torvalds, "Rivoluzionario per caso", 2001)