MokaByte 94 - Marzo 2005 
Sviluppare applicazioni J2EE di grandi dimensioni
Un approccio concreto e affidabile
di
Doriano Gallozzi
Divide et impera, dicevano gli antichi Romani, cioè affrontiamo una realtà articolata e complessa suddividendola in elementi più piccoli e facilmente gestibili. Possiamo applicare questa regola alla progettazione e sviluppo di grandi applicazioni J2EE?

Introduzione
Quando ci si trova a dover affrontare lo sviluppo di applicazioni J2EE di grandi dimensioni, si è portati a pensare che un approccio classico basato su una metodologia di tipo top-down possa risultare efficace. Si cerca quindi di suddividere una realtà complessa in tante sottoparti, per ciascuna delle quali applicare nuovamente il medesimo procedimento di suddivisione. Tutto questo basta a garantirci l'efficacia del tipo di approccio? Forse no, giacchè è di estrema importanza anche la scelta dei criteri secondo cui viene effettuata la suddivisione in sottoproblemi, così come la questione di caratterizzarne le singole parti e i loro componenti, e di definire e gestire le reciproche interrelazioni. Questo è appunto l'obiettivo del presente articolo, e cioè proporre e descrivere un approccio affidabile, produttivo ed efficace al problema dello sviluppo di grandi applicazioni J2EE, discutendo i concetti di base in una prima parte e presentando un esempio concreto nella sezione conclusiva.

 

Applicazioni J2EE di grandi dimensioni
Lo sviluppo di applicazioni J2EE di grandi dimensioni è un processo che oltre alla complessità intrinseca degli obiettivi di business da raggiungere, deve confrontarsi con una ulteriore variabile, appunto le dimensioni dell'intero progetto. Questo fatto, come illustrato in rif.[1], implica la necessità di affrontare le attività di progettazione e sviluppo suddividendo l'intero progetto in progetti più piccoli, quindi più facilmente gestibili. Sempre leggendo rif.[1], viene evidenziato come un approccio vincente consista nell'impiegare la modellazione secondo quanto suggerito da Object Management Group (la Model Driven Architecture, MDA nel seguito, vedi anche rif.[2] e rif.[3]), allo scopo di controllare al meglio la complessità che applicazioni di grandi dimensioni hanno in sè.
Secondo l'approccio MDA, si costruisce un modello di business (Platform Independent Model o PIM), si deriva da esso un insieme di modelli applicativi (Platform Specific Model o PSM) e da essi il codice che costituisce l'applicazione globale (Code Model).


Figura 1 - Model Driven Architecture

In un simile scenario, piuttosto complesse saranno anche le problematiche al contorno, tra cui ad esempio:

  • Dimensioni del team di sviluppo, sarà numeroso e conterrà un gran numero di differenti skill
  • Progettazione, costruzione e gestione di un modello MDA di business di grandi dimensioni
  • Complessità delle attività di integrazione
  • Elevato numero di iterazioni nello sviluppo per raffinamenti successivi
  • Necessità di riuscire a coprire per intero il ciclo di vita della applicazione
  • Necessità e complessità delle attività di personalizzazione richieste


Figura 2
- Impiego di MDA nei progetti

Sfruttando la metodologia MDA si riduce sensibilmente la complessità di quanto si sviluppa, proprio in virtù dello sviluppo per modelli così come previsto da MDA medesima. In aggiunta, suddividendo il modello base costituente il progetto in diversi sotto-modelli (sottoprogetti) si riesce meglio e più efficacemente ad affrontare e parallelizzare differenti task di sviluppo.
Resta però da risolvere un problema di base: individuare in modo opportuno i singoli sottoprogetti e identificarne e gestirne al meglio l'intercomunicabilità. E non si tratta di qualcosa di banale.

 

Package e Subsystems in UML
Dato che parliamo di MDA, metodologia che viene impiegata servendosi del formalismo UML, esaminiamo due importanti concetti del mondo UML, e precisamente i concetti di Package e Subsystem.
Un Package è definito (vedere rif.[4]) come un particolare tipo di costrutto che permette di organizzare gli elementi costitutivi dei modelli (ad esempio classi o use-case) in gruppi, detti appunto Package.
Il problema è che l'utilizzo dei soli Package nello sviluppo di architetture di una certa complessità può non essere sufficiente. Esiste qualcosa di più adatto? La risposta è sì, ed è il Subsystem.
Un Subsystem (rif.[5]) è un particolare tipo di Package, in cui il meccanismo di aggregazione è spinto ad un livello di granularità più elevato rispetto a quello tipico dei Package. In sostanza, un Subsystem può essere pensato come una sorta di Package specializzato, in cui i singoli elementi componenti sono stati selezionati (e quindi aggregati) in base a considerazioni di tipo specifico, quali ad esempio:

  • l'esigenza di dividere il software complessivo in moduli "pluggable"
  • l'opportunità di parallelizzare un progetto complesso attivando simultaneamente lo sviluppo di subtask
  • la necessità di poter disporre di separazioni dovute e motivi di sicurezza (ad esempio in un sistema di pagamenti le informazioni inerenti i diversi livelli salariali devono essere gestite con diversi livelli di sicurezza e legate a diversi data pool).


Considerazioni sull'uso dei Subsystems
Non esiste un numero magico in grado di dirci quale deve essere la cardinalità ideale di ciascun Subsystem in termini di elementi componenti. Esiste però una serie di considerazioni pratiche, dettate dal buon senso e dall'esperienza, ciò che normalmente viene definito "best practices". Vediamone alcune.

  • E' potenzialmente pericoloso pensare di legare il numero dei Subsystem al numero di use-case individuati, sia perchè questo può complicare eccessivamente il modello di business di partenza, sia perchè si corre il rischio di perdere di vista la visione completa del proprio business. E' più indicato costruire il modello di partenza basato sul proprio core-business, individuandone le varie classi e le mutue interrelazioni; solo in uno step successivo si provvederà ad aggiungere quanto derivante dagli use-case
  • Cercare di ridurre la dipendenza tra Subsystem minimizzando il numero delle associazioni esistenti tra essi. L'obiettivo è quello di ottenere un livello di accoppiamento quanto più basso possibile.
  • Tenere presente che un Subsystem dovrebbe costituire sempre e comunque un'unità estremamente maneggevole. Un architect cercherà sempre il compromesso ideale tra maneggevolezza - riduzione della dimensione dei Subsystem - e agilità - riduzione del numero di interdipendenze tra un Subsystem e gli altri.

Progettare per Subsystems
Normalmente, un architect dovrebbe produrre il diagramma dei Subsystem con le relative interdipendenze durante le prime fasi dello sviluppo, di solito al termine della fase di disegno dello Use Case Diagram.

  • Lo scopo principale di un diagramma delle interdipendenze tra Subsystem è:
    identificare con chiarezza gli elementi (classi) di interfaccia tra Subsystem, tenendo a mente quelli che potranno essere futuri cambiamenti e relative analisi di impatto
  • stabilire i livelli di dipendenza tra Subsystems, al fine di poter poi agevolmente determinare quale possa essere la sequenza di sviluppo di essi stessi

Non esiste, ad oggi, una notazione dedicata per disegnare tali diagrammi, neppure in UML 2.0. D'altra parte, è possibile servirsi della notazione disponibile per i Class Diagram, in cui ciascun Subsystem viene identificato dallo stereotipo <<subsystem>>, così come esemplificato nella figura 3.


Figura 3 - Dipendenze tra Subsystems
(clicca sull'immagine per ingrandire)


Un esempio
Un esempio concreto potrà forse chiarire meglio i concetti presentati. Il nostro obiettivo è quello di mostrare come una applicazione di partenza - con tutti i suoi elementi - possa essere "vista" e utilizzata da altre applicazioni proprio grazie al concetto dei Subsystem. Essi quindi permettono, come conseguenza, di assemblare applicazioni relativamente semplici al fine di pervenire a strutture via via più complesse, fino a costituire l'applicazione finale, quantunque essa possieda dimensioni ragguardevoli.
Per la realizzazione del nostro esempio utilizziamo lo strumento Compuware OptimalJ (rif.[6]), tool di sviluppo J2EE completamente aderente alla metodologia MDA e ai concetti di Package e Subsystem.
Partiamo quindi da un semplice modulo applicativo che descrive la gestione di una biblioteca e dei libri che essa possiede. Il Class Diagram del nostro modulo applicativo è rappresentato in figura 4. Si noti in particolare l'elemento di tipo "subsystem", che costituisce un "punto di raccolta" per tutti gli elementi del modulo applicativo che appartengono a quel determinato Subsystem. Nel nostro caso appartengono al Subsystem "book" le classi Book e Library e anche i loro attributi e relativi tipi complessi Address e BookType.

 


Figura 4 - Class Diagram applicazione Book
(clicca sull'immagine per ingrandire)

Durante questa prima fase può essere richiesto arricchire il modulo applicativo di ulteriori funzionalità. Ad esempio, definire e codificare un metodo di business in un session bean creato a fronte del modello di business contenuto nel class diagram.
Rivolgendo la nostra attenzione al Session Component LibraryDetails, aggiungiamo ad esso un business method numberOfBooks,destinato a restituire il numero di libri presenti nella biblioteca (figura 5).


Figura 5 - Modellazione di un Business Method
(clicca sull'immagine per ingrandire)

Effettuiamo una veloce fase di collaudo del nostro modulo Book destinata a inserire alcuni dati significativi, ad esempio le informazioni relative ad una biblioteca e ai libri che le appartengono, (figure 6 e 7).


Figura 6 - Creazione di una biblioteca

 


Figura 7 - Definizione dei libri della biblioteca

A questo punto siamo pronti per rendere "pubblico" il nostro modulo Book e farlo diventare a tutti gli effetti un Subsystem condivisibile e utilizzabile da altri moduli applicativi (altri Subsystem).
A tal fine effettuiamo come prima cosa la pubblicazione del modulo Book, cosa che come risultato provocherà la costruzione di un componente book.jar contenente tutti gli "ingredienti" necessari alla pubblicazione (figura 8)


Figura 8 - Pubblicazione del Subsystem Book

 

Successivamente predisponiamo la nostra applicazione in formato pronto per il deployment, e quindi costruiamo l'archivio EAR (enterprise archive repository).
Gli oggetti che dovranno essere "visibili" a tutti i moduli applicativi che devono utilizzare il Subsystem Book saranno quindi book.jar, risultante dalla pubblicazione, e book.ear, contenente gli artifatti applicativi.

Adesso immaginiamo che un differente team di sviluppo stia lavorando ad un altro semplice modulo applicativo, destinato a implementare e gestire le credenziali di una persona che utilizza i libri della biblioteca.
Il nostro Class Diagram conterrà una sola classe, Reader (figura 9).


Figura 9 - Class Diagram applicazione Reader

 

Come passo successivo, effettuiamo il "mount" via Filesystem degli archivi book.ear e book.jar, cosa che, in un caso reale, richiederà che essi siano resi disponibili in una qualche locazione, anche remota .


Figura 10 - Mount del Subsystem Book
(clicca sull'immagine per ingrandire)

 

Il risultato di tale operazione è che, passando a esaminare il Domain Model del modulo Reader che stiamo sviluppando, ci accorgiamo che adesso "vediamo" anche l'intero Subsystem Book, (si noti la presenza del piccolo lucchetto nero, a indicare che Book può essere referenziato, ma non modificato).


Figura 11 - Class Diagram contenente il Subsystem Book

Ora il nostro modulo applicativo Reader potrà utilizzare tutto quanto reso disponibile nel Subsystem Book. Ciò è ulteriormente evidente dalla figura 12, in cui se selezioniamo il modulo Subsystem di Reader notiamo che afferisce, come Subsystem utilizzato, proprio a Book.


Figura 12 -
Subsystem Reader utilizza Subsystem Book

Subsystem Reader può utilizzare Subsystem Book in vari modi. Ad esempio si può dichiarare uno degli attributi di Reader in modo che sia di un tipo specificato in Book, ad esempio di tipo Address (figura 13).


Figura 13 -
Riutilizzo definizione di tipi tra Subsystem

Si può poi definire una associazione tra i due Subsystem, ad esempio tra la classe Reader e la classe Book (figura 14)


Figura 14 - Associazioni tra Subsystem
(clicca sull'immagine per ingrandire)

Allo stesso modo, è possibile aggiungere a uno dei componenti di Reader (ad esempio al suo session component ReaderDetails) dei business method che referenziano le operazioni presenti in Book. Nella figura 15 si nota l'implementazione di un business method che calcola e restituisce il numero di libri associati ad un lettore.


Figura 15 -Implementazione di un Business Method
(clicca sull'immagine per ingrandire)

 

A questo punto, compilata l'applicazione Subsystem Reader e immaginando che il Subsystem Book sia "up and running", cioè attivo e in esecuzione, facciamo partire anche il Subsystem Reader.


Figura 16 - Subsystems Book e Reader
(clicca sull'immagine per ingrandire)

Attiviamo la pagina di gestione del generico lettore (Reader), per crearne uno nuovo e la prima cosa che notiamo è la presenza nell'attributo address degli elementi caratteristici del tipo complesso Address definito in Book (figura 17).


Figura 17 - Creazione nuovo lettore
(clicca sull'immagine per ingrandire)

 

Sempre sulla pagina di gestione del generico lettore (Reader), se facciamo clic sul tab Reader.book, attiviamo l'associazione tra Reader e Book, e questo ci permette di selezionare un libro da associare al lettore corrente. Si noti che i libri disponibili sono - ovviamente - quelli che avevamo precedentemente inserito in figura 7 (figura 18).


Figura 18 - Associazione tra Subsystem
(clicca sull'immagine per ingrandire)

Immaginando di avere associato a questo lettore due libri e sempre partendo dalla pagina di gestione del generico lettore (Reader), attiviamo la funzione booksPerReader già vista in figura 15, e che ci restituisce esattamente il numero di libri associati al lettore, dimostrando un riutilizzo, in ambito Subsystem Reader, dei metodi definiti nel Subsystem Book (figura 19)


Figura 19 - Calcolo dei libri associati al lettore
(clicca sull'immagine per ingrandire)

 

Conclusioni
Affrontare la progettazione e lo sviluppo di applicazioni J2EE di grandi dimensioni è qualcosa di complesso e articolato. E' certamente consigliabile cercare di suddividere l'ambito che si intende affrontare in sottoprogetti di maggiore semplicità e maneggevolezza. In questo articolo si è cercato di illustrare come il concetto di Subsystem, proposto e definito in ambito UML permetta di approcciare questo tipo di problematica in modo concreto e affidabile e si è altresì cercato, attraverso un esempio semplice ma significativo, di dimostrare quanto possa essere potente in pratica l'uso di Subsystem per definire e implementare differenti parti del proprio sistema, salvo avere la possibilità di interrelare i vari Subsystems. E' un modo per affrontare realtà complesse suddividendo il problema principale in sottoproblemi, e affrontandoli separatamente in modo efficace, produttivo e rapido. Think big, start small, move fast, non dimentichiamolo!

 

Bibliografia
[1] H. Tai, K. Mitsui, T. Nerome, M. Abe, K. Ono, and M. Hori, "Model-driven development of large-scale Web applications", on line copy su http://www.research.ibm.com/journal/rd/485/tai.html

[2] Mokabyte - "Uso della Model Driven Architecture nello Sviluppo di Applicazioni J2EE", parte 1 http://www.mokabyte.it/2004/07/mda-1.htm e parte 2 http://www.mokabyte.it/2004/09/mda-2.htm)

[3] Compuware White Papers "Addressing the business challenges of Java/J2EE development", on line copy su http://javacentral.compuware.com/members/downloads/pdf/OJj2ee.pdf

[4] Scott W. Ambler, "UML Package Diagramming Guidelines", on line copy su http://www.agilemodeling.com/style/packageDiagram.htm

[5] Jan JÄurjens "Formal Semantics for Interacting UML subsystems" online copy su http://www4.in.tum.de/~juerjens/papers/fmoods02Talk.pdf

[6] Compuware Lab "OptimalJ Community" - http://javacentral.compuware.com

Doriano Gallozzi è nato a Roma nel 1964 e si è laureato in Ingegneria Elettronica (indirizzo Informatica) nel 1989. Da sempre interessato a problematiche di analisi, progettazione e sviluppo di Sistemi Informativi, ha lavorato per diversi anni in aziende del settore informatico italiano (gruppo ENI, gruppo Telecom Italia), dove ha acquisito diverse esperienze tanto nel campo della progettazione e sviluppo del software (in ambiente M/F come in ambiente distribuito) quanto nel campo dei RDBMS (DBA su diversi progetti per clienti finali quali Telecom Italia). Da gennaio 2000 lavora nella Divisione Prevendita di Compuware Italia. La sua attività verte principalmente sulla piattaforma J2EE e tecnologie correlate, ma anche su ambiti tecnologici quali l'Enterprise Application Integration, i Portali Web, gli strumenti di Business Process Modeling.

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it