Organizzazione di Progetti Complessi con Maven

Maven è un progetto della Apache Software Foundation che semplifica la gestione e lo sviluppo del codice specialmente Java. Molto simile al tool Ant, che può essere considerato il suo progenitore, ma offre diverse altre funzionalità (avanzate) quali:

  • Standardizzazione della struttura del progetto e del processo di compilazione.
  • Esecuzione automatica dei test.
  • Gestione e download automatico delle librerie necessarie al progetto con risoluzione delle eventuali dipendenze (transitive).
  • Possibilità di estendere le funzionalità tramite l’utilizzo di plugin.

Per un approfondimento di tali concetti si rimanda ai post Organizziamoci con Maven e Creare una Web Application con Maven.

Lo scopo del presente articolo è invece quello di comprendere come sia possibile organizzare un progetto java complesso, tipicamente di tipo Java EE, utilizzando tale strumento. Maven, infatti, possiede una caratteristica molto utile allo scopo, ovvero la possibilità di organizzare il codice in moduli distinti ma interdipendenti.

Progetti Modulari

L’organizzazione di progetti in moduli si realizza semplicemente definendo un progetto parent nel cui POM (denomitato parent POM o top level POM) sono referenziati i sotto moduli attraverso il tag <moduls>.

Analogamente i moduli referenziano il progetto parent attraverso il tag <parent> nel modo seguente.

Dal punto di vista della strutturazione del progetto su file system i due moduli corrispondono a due sub-directory construttura del file systemtenute nella directory principale associata al parent project (javaboss-parent nel nostro esempio). Questa struttura non è obbligatoria, nel senso che è anche possibile mantenere il parent project ed i due moduli all’interno di una stessa directory comune. Ma in questo caso dovrà essere utilizzato il tab <relativePath> in tutti i POM.

Nel progetto parent il valore del packaging è POM, proprio ad indicare il fatto che si tratta di un contenitore senza codice e che quindi non produce un jar. Diversamente i due moduli inclusi, come si intuisce dal come, avranno come packaging i valori war e jar.

Un’altra caratteristica della scomposizione in moduli del progetto è l’ereditarietà che, in maven, corrisponde al fatto che le configurazioni definite nel parent POM vengono automaticamente ereditare dai moduli. In fase di elaborazione, infatti, maven esegue un merge del POM corrente col parent POM producendo quello che viene indicato come Effective POM. Tale informazione può anche essere visualizzata con il comando mvn help:effective-pom eseguito nella cartella di uno dei due moduli o, in Eclipse, aprendo il file pom.xml col Maven POM Editor e selezionando il tab Effective POM.

L’informazione minima che deve essere definita nel POM di un modulo è l’artifactId, che è ovviamente differente per ogni modulo. Nel nostro caso, ad esempio, il modulo javaboss-modul-jar eredita dal parent le informazioni relative al groupId ed alla versione, nonché la dipendenza da JUnit, il plug-in maven-compiler-plugin e la sua configurazione.

Di seguito è riportato un elenco degli elementi che un POM eredita dal suo parent POM:

  • gli identificatori (almeno uno tra gruppoId o artifactId deve essere sovrascritti);
  • le dipendenze;
  • sviluppatori e collaboratori del progetto;
  • l’elenco dei plugin;
  • l’elenco dei report;
  • l’esecuzione dei plugin (le esecuzioni con ID corrispondenti sono unite);
  • la configurazione dei plugin.

Organizzazione dei Progetti

Una volta introdotti gli aspetti tecnici rilevanti allo scopo affrontiamo il problema su come organizzare “logicamente” un progetto mediamente complesso. In generale esistono modi differenti di raggruppare il codice ma in generale possiamo distinguere due differenti filosofie:

  • Module by Class Type
  • Module by Functionality Area

Nel primo caso il progetto può essere suddiviso in sottomoduli dove ogni modulo è utilizzato per raggruppare tutte le classi che comprendono un livello nell’architettura del progetto. Ad esempio, un modulo potrebbe contenere tutte le classi che compongono i livelli di servizio (service layer) o di persistenza (persistence layer) del progetto oppure un modulo potrebbe contenere tutte le classi di modello (model layer). Esempi di moduli di questo tipo sono:
– app-model
– app-utils
– app-repository
– app-services
– app-web
– app-ear

Nel secondo caso i sottomoduli del progetto sono organizzati per area funzionale ovvero l’applicazione è suddivisa in componenti (component based) che includono tutti i layer necessari al suo funzionamento, inclusi i bean del modello, il livello di servizio, il repository ecc. Esempi di moduli di questo tipo sono:
– app-utils
– app-user
– app-audit
– app-auth
– app-integration
– app-web
– app-ear

La tabella seguente mostra i pro ed i contro dei due approcci:

Module by Class Type Module by Functionality Area
PRO Semplice da manutenere

L’ordine di build è diretto

I moduli sono altamente disaccoppiati

È più semplice riutilizzare un’area funzionale di codice in altri progetti

CONTRO Dipendenze circolari

Classi con responsabilità totalmente diverse sono mescolate insieme rendendo difficile riutilizzare la funzionalità (in altri progetti)

L’ordine di build potrebbe essere complesso a causa delle sue dipendenze

Conclusioni

La scelta del tipo di organizzazione è chiaramente dipendente dalle esigenze del progetto in quanto nessuna delle due mostrate è l’ottima al 100%. Analizzando gli esempio si nota però che possono essere identificati moduli comuni ad entrambe le strategie. Questo perché determinate porzioni di codice ha senso tenerle unite un un modulo comune, come ad esempio quello per l’accesso al db. In generale quindi i due approcci possono essere mixati per ottenere la struttura che meglio si adatta al progetto.

© 2019 Java Boss - Theme by HappyThemes