Implementare il business service layer con Spring è un‘ottima scelta per la flessibilità e la modularità che il framework conferisce alla nostra applicazione ma anche per la sua semplicità d‘uso. Iniziamo a vedere come questo possa essere messo in pratica.
Introduzione
Nel precedente articolo abbiamo iniziato la trattazione del business service layer ed abbiamo visto come Spring possa essere oggi una delle migliori scelte per la sua implementazione. I concetti che sono alla base di Spring sono stati introdotti nel precedente articolo. Ora vedremo come si può implementare nella pratica lo strato di business sfruttando le caratteristiche del framework. È bene sottolineare che la trattazione è assolutamente indipendente dalla tecnologia con cui alla fine si deciderà di implementare il layer di presentation, con Jakarta Struts, con JSF o con lo stesso Spring. Lo scopo è quello di illustrare come si possa realizzare uno strato di servizi di business assolutamente riusabili in qualsiasi contesto applicativo. Questo obiettivo è facilmente raggiungibile con Spring visto che per sua natura consente la scrittura di applicazioni estremamente modulari e di componenti realmente riusabili.
Dal momento che abbiamo già trattato il persistence layer ed abbiamo già discusso di Hibernate si farà l’assunto che il layer di servizi utilizzi a sua volta le interfacce esposte da un layer di persistenza implementato con Hibernate.
L’architettura dell’applicazione
Come si era già detto, uno dei principi fondamentali per l’implementazione di uno strato di business realmente riusabile è quello di realizzare un insieme di “servizi” che espongono le loro funzionalità al client mediante interfacce ben definite. Nella nostra discussione il client è lo strato di presentazione. Il componenti del layer di presentation, quelli che svolgono la funzione di controller in particolare, avranno esclusivamente un riferimento a una interfaccia. Ciò significa che il presentation layer potrà rimanere del tutto invariato al modificarsi dell’implementazione dei metodi di business definiti nell’interfaccia stessa. Questo principio fondamentale di una buona programmazione a oggetti ha come presupposto che in qualche modo i componenti dell’applicazione riescano ad acquisire i riferimenti agli oggetti di implementazione delle interfacce di business. Ciò significa che da qualche parte è necessario andare a reperire i riferimenti alle classi di implementazione delle interfacce di business. A tale scopo si usa spesso un ServiceLocator ossia una classe che implementa il pattern ServiceLocator e che consente di accentrare in un unico componente tutte le operazioni di lookup necessarie a reperire i componenti desiderati. Sebbene il ServiceLocator sia un pattern che conferisce una struttura più solida e manutenibile alla propria applicazione, esso costituisce pur sempre un aggravio di operatività e di scrittura di codice per una operazione assolutamente ripetitiva e a scarso valore aggiunto. Con Spring tutto ciò non è necessario in quanto, come già detto nel precedente articolo parlando del principio dell’Inversion Of control, è Spring che si occupa di iniettare le dipendenze tra le classi della nostra applicazione secondo una logica opposta a quella in cui sono le classi stesse ad andare a reperirsi i riferimenti agli oggetti di cui hanno bisogno.
L’architettura applicativa di una applicazione che utilizza Spring per l’implementazione del proprio strato di business può essere quindi molto semplificata rispetto a quelle tradizionali.
In pratica, i componenti di controllo del web layer, le Action di Struts o i managed bean di JSF, avranno un riferimento a una interfaccia esposta dal layer di business. Il riferimento al bean di implementazione dell’interfaccia sarà iniettato nel componente di presentation layer dal container IoC di Spring senza che il primo debba andare a compiere alcuna operazione. Ciò consente innanzitutto di scrivere meno codice ma rende soprattutto i layer ben distinti e poco accoppiati, caratteristica fondamentale per la corretta realizzazione di un’applicazione.
Nella nostra architettura avremo quindi una interfaccia di business (che chiameremo ServiceInterface) che espone al presentation layer i metodi di business che saranno implementati da una classe (un comune POJO, che chiameremo ServiceInterfaceImpl). Le dipendenze tra i vari oggetti sono tutte iniettate dal container IoC di Spring.
In Figura 1 è rappresentato il diagramma delle classi dell’architettura descritta contenente per semplicità solo gli elementi essenziali alla comprensione di quanto detto. Come si vede, il componente di presentation layer “conosce” esclusivamente l’interfaccia di business e non la sua implementazione. Stessa cosa accade tra la classe di implementazione dell’interfaccia di business e lo strato di persistenza. I layer sono ben distinti e non vi è la necessità da parte di nessuno dei componenti dell’architettura di andarsi a reperire riferimenti agli oggetti degli altri layer: a questo, come vedremo, pensa Spring.
Figura 1 – Architettura applicativa del business layer con Spring.
A parte i dettagli implementativi, la cosa importante da notare è come ogni layer sia visibile all’altro solo mediante interfacce, allo scopo di evitare pericolose dipendenze tra layer distinti che potrebbero condurre a seri problemi nel caso in cui uno dei layer venisse pesantemente modificato.
La struttura descritta è la più semplice possibile da realizzarsi con Spring, ma il fatto che sia semplice non significa che non sia efficiente e ben fatta.
A volte è facile trovare in applicazioni costruite con Spring vere e proprie classi di tipo Business Delegate che espongono metodi di business allo strato di presentation utilizzando al loro interno le interfacce dei servizi del layer di business. Questa seconda possibilità introduce un ulteriore livello di astrazione tra presentation layer e business layer e può essere presa in considerazione in situazioni nelle quali il livello di business è così articolato che si preferisce mascherarne la complessità con l’utilizzo di BusinessDelegate. In realtà si può osservare che una ServiceInterface assolve di per se’ ai compiti di BusinessDelegate quindi in realtà tra le due architetture non c’è una sostanziale differenza; oltre a ciò spesso quello che succede è che si viene a creare un rapporto uno-a-uno tra classi business delegate e interfacce di business il che vanifica totalmente il ruolo dei BusinessDelegate stessi.
Inoltre è bene ricordare che Spring ha in se’ i meccanismi che consentono di iniettare le dipendenze nei componenti di presentation layer più diffusi, come le Action di Struts o i managed bean di JSF, semplicemente configurando opportunamente l’applicazione. L’introduzione di una classe di BusinessDelegate costringe invece a dover effettuare nel codice dello stesso delle operazioni di acquisizione dei bean dal contesto di Spring, magari mediante l’uso di ServiceLocator. Ciò, a mio parere, contraddice il concetto stesso di IoC ed è quindi qualcosa di poco pulito da un punto di vista architetturale.
La configurazione del contesto di Spring
Dopo aver dato un’occhiata all’architettura dell’applicazione, vediamo come sia possibile realizzare tutto ciò con Spring soffermandoci solo sugli elementi di interesse per la nostra discussione. La configurazione di una applicazione che realizzi lo schema presentato è quanto di più semplice possa esistere.
Innanzitutto è necessario inizializzare il contesto di Spring; lo facciamo inserendo nel web.xml dell’applicazione l’opportuno listener:
org.springframework.web.context.ContextLoaderListener
A questo punto è necessario configurare opportunamente il contesto di Spring definendo queste famose dipendenze tra gli oggetti di cui abbiamo tanto parlato. È possibile farlo andando a scrivere un file XML che per una applicazione web solitamente si chiama applicationContext.xml e si trova nella cartella /WEB-INF dell’applicazione (nel web.xml è possibile configurare un nome e un percorso alternativo per questo file).
Il file di configurazione di Spring è molto semplice in quanto consiste quasi esclusivamente nella definizione di una serie di bean e delle loro dipendenze. Vedremo il file nel suo insieme quando completeremo la discussione; per ora ci basta analizzare le due sezioni qui di sotto estrapolate allo scopo di vedere come vengano definiti i bean di implementazione del DAO e dell’interfaccia di business e come in quest’ultimo venga “iniettata” la dipendenza al DAO stesso realizzando quanto descritto in Figura 1.
È il container di Spring che istanzia i bean in questione e che assegna alla proprietà dao della classe it.mokabyte.api.service.impl.ServiceInterfaceImpl un riferimento alla classe it.mokabyte.api.dao.hibernate.HibernateDAOImpl.
Le classi in questione sono semplici POJO che implementano le interfacce definite e sono riportate qui di seguito nella loro struttura base.
Ecco l’interfaccia e il bean di implementazione del Service
package it.mokabyte.api.service; public interface ServiceInterface { //business service methods } package it.mokabyte.api.service.impl; import it.mokabyte.api.dao.DAOInterface; import it.mokabyte.api.service.ServiceInterface; public class ServiceInterfaceImpl implements ServiceInterface { private dao DAOInterface; public UserServiceImpl() { } public void setDao(DAOInterface dao) { this.dao = dao; } public DAOInterface get Dao () { return dao; } }
Ed ecco l’interfaccia e il bean di implementazione del DAO
package it.mokabyte.api.dao; public interface DAOInterface { //dao methods ............ } package it.mokabyte.api.dao.hibernate; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; public class HibernateDAOImpl extends HibernateDaoSupport implements DAOInterface { //dao implementation methods }
Quello che manca per completare la struttura di Figura 1 è capire come iniettare il riferimento al bean di implementazione dell’interfaccia di business dentro al componente di presentation layer.
In [6] avevamo già mostrato come farlo in maniera molto semplice utilizzando JSF per lo strato di presentation. Senza addentrarci in una discussione che esula dallo scopo dell’articolo, ma solo a scopo di completezza, in questo caso supponiamo che per lo strato di presentation si sia usato Struts e che si voglia iniettare il riferimento alla classe di implementazione dell’interfaccia di business in una Action.
A tale scopo occorre inserire nello struts-config.xml il plugin di Spring che consente a Struts di avere un riferimento al contesto di Spring:
value="/WEB-INF/applicationContext.xml"/>
Inoltre per ogni Action che usa Spring va ridefinito il mapping utilizzando la classe org.springframework.web.struts.DelegatingActionProxy come segue:
type="org.springframework.web.struts.DelegatingActionProxy" name="form" scope="request" parameter="method">
Infine va definita la classe Action come bean nel contesto di Spring e va iniettata in essa il riferimento al bean di implementazione dell’interfaccia di business:
La struttura di Figura 1 è così realizzata: ogni layer vede il layer adiacente esclusivamente tramite interfacce e tutti i riferimenti alle classi di implementazione delle interfacce stesse vengono forniti ai diversi componenti dal container di Spring senza la necessità di scrittura di una sola riga di codice.
Conclusioni
Nel presente articolo abbiamo visto come sia possibile strutturare un’applicazione web utilizzando Spring per l’implementazione dello strato di business. Nel prossimo articolo completeremo la discussione sul business layer vedendo più nel dettaglio anche la configurazione dello strato di persistenza e l’interazione con Hibernate. Quello che si voleva già da ora trasmettere è come sia estremamente semplice costruire con Spring uno strato di servizi riusabili da esporre mediante interfacce a un qualsivoglia strato di presentation senza che ciò aggiunga alcuna complessità alla nostra applicazione.
Riferimenti
[1] Rod Johnson, “Expert one-on-one. J2EE Design and Development”, 2002, Wrox Press
[2] Rod Johnson – Juergen Holler, “Expert one-on-one. J2EE Development without EJB”, 2004, Wrox Press
[3] Rod Johnson, “Introduction to the Spring Framework 2.5”, Ottobre 2007, The ServerSide.Com
[4] Giovanni Puliti, “Il programmatore e le sue API – VI parte: L’architettura del sistema”, MokaByte 132, Settembre 2008
[5] Giovanni Puliti, “Il programmatore e le sue API – VII parte: Ancora sull’uso dei pattern nell’architettura”, Mokabyte 133, Ottobre 2008
[6] Alfredo Larotonda “JSF in azione – IV parte: Integrazione”, Mokabyte 125, Gennaio 2008