Maven: organizzazione gerarchica dei progetti

Uno degli aspetti più interessanti di maven come strumento di automazione del processo di build è sicuramente la possibilità di organizzare i nostri pom secondo una struttura gerarchica.

Qualsiasi progetto maven nasce automaticamente come figlio del Super Pom, configurazione di base di maven stesso e contenuto nelle sue librerie.
In questo articolo vedremo come usare lo stesso approccio per organizzare i nostri progetti, riducendo drasticamente la ridondanza delle configurazione, ottimizzando i tempi e creando dei veri e propri set di librerie che costituiscano lo Stack tecnologico su cui siano sviluppati i nostri prodotti.

Iniziamo ad analizzare le diverse problematiche che andremo a risolvere con questo approccio:

  • Ridondanza della configurazione dei nostro sistema di continuous integration: tutti gli IP/Url , i repository e qualsiasi altro riferimento globale al sistema verranno centralizzati in un unico punto, evitando di doverli duplicare in tutti i pom e consentendo un agile punto di accesso per eventuali modifiche.
  • Ridondanza nell’utilizzo dei plugin comuni a tutti i progetti quali compilazione, gestione dei sorgenti, creazione javadocs, etc.
    anche in questo caso cercheremo di localizzare i fattori comuni nei pom più adatti a gestirli in maniera agevole e centralizzata
  • Duplicazione e disallineamento delle versioni delle librerie, terze parti e non, utilizzate in maniera trasversale su più progetti: con questo approccio è infatti possibile centralizzare lo stack tecnologico che si desidera utilizzare, così come modificare in maniera veloce l’impiego di una nuova versione di una data libreria su più progetti senza la necessità di individuare quali di questi ne facciano uso e modificarli puntualmente

Ora cerchiamo invece di riassumere con un diagramma la struttura gerarchica che vorremmo ottenere:

Poms Hierarchy

Struttura gerarchica dei progetti

Come potete notare vogliamo avere un un pom globale, definito per comodità Global Parent Pom, dal quale discendano con una relazione padre-figlio (diretta o indiretta tramite altri pom intermedi) tutti gli altri pom dei nostri progetti.

In questo file andremo ad inserire tutte le configurazioni di carattere globale, che vogliamo vengano assolutamente ereditate da qualsiasi progetto venga creato all’interno della nostra piattaforma.

Cosa inseriremo in questo progetto:

  • Tutti i riferimenti ad IP/Url utilizzati all’interno della catena di build, quali sistemi di BugTracking, di Continuous Integration, sistemi di versionamento, etc. Centralizzare questo tipo di informazioni in un unico punto, e propagarle negli altri progetti sotto forma di variabili ci consente di avere un unico punto di modifica in caso di cambiamento. Pensate ad esempio di dover reinstallare il vostro repository Maven aziendale cambiandogli IP, oppure se decideste di cambiare sistema di versioning passando da SVN a GIT. Se queste configurazioni non fossero opportunamente centralizzate e parametrizzate, uno qualsiasi di questi cambiamenti vi costringerebbe a modificare uno ad uno i vostri progetti e a ri-generarli con una nuova versione. Utilizzando delle variabili ereditate dal pom padre tutto questo non sarà più necessario.
  • Tutti i plugin che sicuramente utilizzerete in tutti i progetti e le loro relative configurazioni. Un buon approccio è inserire qui sia la compilazione dei sorgenti, ma anche la generazione automatica della loro documentazione e l’impacchettamento (separato!) del codice sorgente. In questo modo queste procedure saranno automaticamente disponibili in ogni nuovo progetto, senza bisogno di replicarle ogni volta. Vi consiglio di parametrizzare anche la versione del compilatore utilizzato, in modo da poterla facilmente cambiare in caso di upgrade.
  • I riferimenti al DistributionManagement, ovvero a quale repository maven remoto fare riferimento per la fase di deploy e con quale politica (Release/Snapshot) in modo che tutti i progetti risultino automaticamente deployati sul repository corretto senza bisogno che gli sviluppatori debbano inserire i dati in ogni progetto.
  • Qualsiasi altra variabile di uso globale possa essere utile parametrizzare come ad esempio il charset da utilizzare, la versione (target/source) di java.

Cosa NON inseriremo in questo progetto:

  • dipendenze (e/o dependency-management) relative ai diversi progetti. Questo pom va mantenuto il più globale possibile, e inserire a questo livello delle dipendenze su altri artifact può creare problemi di compatibilità in caso di progetti medio/grandi.
    Vedremo ora come il ruolo di gestione delle librerie sarà delegato ad un altro pom.

Abbiamo quindi compiuto il primo passo, creando un progetto contenitore delle impostazioni globali che vogliamo distribuire su tutti i nostri artifact.

Soffermiamoci ora su come organizzare la struttura del codice vero e proprio.
Supponiamo di avere una serie di progetti di librerie API e una serie di progetti di Frontend che producono l’ambiente finale utilizzando tali API come business logic.

Quello che vogliamo andare a creare è un nuovo pom per gestire tutte le configurazioni comuni ai progetti API (API Parent Pom nel nostro schema) e un altro (FE Parent Pom ) analogo per il frontend.

Nel primo pom andremo a creare l’intera gestione delle dipendenze di tutti gli artifact delle nostre API.
Centralizzare la dichiarazione delle dipendenze tramite dependencyManagement consente di non dover dichiarare la versione di ogni libreria in ogni singolo progetto, evitando cosi che la librerie si disallineino se un progetto ha più attività di sviluppo rispetto ad un altro.

The dependency management section is a mechanism for centralizing dependency information. When you have a set of projects that inherits a common parent it’s possible to put all information about the dependency in the common POM and have simpler references to the artifacts in the child POMs.

Questa centralizzazione aiuta anche a velocizzare i rilasci, in quando, se si cambiassero più dipendenze in un colpo solo , tutte le modifiche potranno essere fatte in un unico punto.

Lo scenario ideale per questa struttura è una serie di librerie API sviluppate internamente dal team, tutte basate sullo stesso stack tecnologico (tipicamente un framework di sviluppo come Spring ) composto da una serie aggiuntiva di librerie terze parti.
Questo approccio comporta il tenere aggiornate e allineate sia le librerie del framework sia quelle sviluppate dal team, consentendo di aggiornare le diverse versioni in maniera trasversale su tutti i progetti API.
Centralizzando il dependency management in un pom esterno, si evita la dichiarazione della versione delle librerie. In questo modo, quando una nuova libreria viene rilasciata, è sufficiente aggiornare la sua versione nel dependency management per propagare il nuovo artifact su tutti i progetti, senza la necessità di informare gli sviluppatori di aggiornare tutti i loro progetti con la nuova versione.

Al contrario, qualora uno sviluppatore volesse freezare la versione di riferimento di una data libreria (ad esempio per problemi di regression con una nuova versione), sarebbe comunque libero di dichiarare la versione desiderata nel suo progetto, sovrascrivendo così le impostazioni offerte dal parent-pom.

Ovviamente, l’approccio del dependencyManagement può essere tranquillamente esteso all’utilizzo di plugin, sia a livello di pluginManagement che di dichiarazione di esecuzioni comuni, all’impiego di variabili da propagare su tutti i progetti figli, o a qualsiasi altra impostazione, in maniera analoga a quanto fatto con il Global parent pom

Infine nel nostro schema abbiamo riportato anche un ramo di gerarchia analogo anche per gli artifact di frontend. I concetti sono gli stessi, ma si voleva solo illustrare come un progetto potesse sia dipendere direttamente dal Global parent pom, piuttosto che da un altro parent intermedio (FE Parent Pom ) dedicato frontend.

Riassumendo con questo approccio abbiamo generato all’interno del nostro progetto tre livelli di configurazione con diversi scope:

  • Global parent pom : visibilità globale su tutti i progetti, qualsiasi artifact erediterà le proprietà di questo progetto
  • API/FE parent pom: visibilità ristretta al progetto, tutti gli artifact di tale area erediteranno sia i settaggi globali che quelli ristretti (che possono,qualora servisse, fare override di quelli globali)
  • Project pom: visibilità locale, impostazioni valide per il singolo artifact, ereditano sia dai settaggi globali sia dai ristretti e possono fare override diei valori di entrambi.

In un ottica di Roles & Responsibilities, possiamo ipotizzare che:

  • Il pom di livello globale sia gestito dal team di CM e contenga tutti gli standard di build/deploy più le varie configurazioni relative all’infrastruttura di Continuous Integration
  • Il pom di progetto sia gestito dai team leader del progetto stesso, consentendo di decidere con quali librerie terze parte operare e tenere sotto controllo i rilasci delle diverse versioni.
  • I pom delle singole librerie siano il campo d’azione degli sviluppatori per organizzare il loro codice secondo gli standard, ma avendo anche libertà d’azione (la possibilità di fare override) per testare o sviluppare con nuove librerie prima di ufficializzarle.
Pubblicità

5 pensieri su “Maven: organizzazione gerarchica dei progetti

  1. Pingback: Artifactory: configurazione client maven | Around the Code

  2. Pingback: Video Review: Getting Started with Apache Maven | Around the Code

  3. Pingback: Unit and Integration tests coverage with SonarQube and Jacoco | Around the Code

  4. Pingback: Environment templates – part 1 – Create your templat | Around the Code

  5. Pingback: Pathfinder – DependencyManagement Analyzer | Around the Code

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...