Automi e Gestione dello Stato

E’ abbastanza comune in applicazioni enterprise la necessità di dover gestire lo stato degli oggetti di business. Si pensi ad esempio ad un documento contabile, una fattura o al classico ordine di acquisto. Oggetti che vengono inizialmente creati in stato di “bozza” e vi rimangono sino a quando l’utente non ne termina la lavorazione per renderlo disponibile ad una fase successiva di processamento.

La gestione degli stati e soprattutto delle transizioni tra uno stato ed il successivo può essere realizzato in differenti maniere. La soluzione più semplice e meno flessibile è quella di gestirla da programma, mentre la più complessa ma di facile manutenibilità è l’utilizzo di sistemi di Business Process Management (BPM) o di workflow come JBPM o Activiti.

L’utilizzo di tali sistema è spesso la soluzione che più spaventa e per questo frequentemente quello che accade è che si preferisce realizzare la gestione degli stati attraverso apposite routine di programma perché, erroneamente, si ritiene siano più semplici da manutenere. In questo post vogliamo presentare una soluzione intermedia, che implica l’utilizzo di un Automa a Stati Finiti.  Una versione più semplice ed abbordabile di sistema di workflow, che consente comunque di descrivere in modo formale il comportamento atteso da una applicazione.

Il Processo

La soluzione che descriviamo nel post implica l’utilizzo del framework open source Automata i cui sorgenti sono disponibili su GitHub e le dipendenze su Nexus Sonotype. Come caso di studio consideriamo la gestione di un ordine di acquisto (PO o purchase order) descritto nel wiki del progetto Automata ed il cui processo di gestione è descritto dal flusso mostrato nell’immagine seguente:

Sostanzialmente il PO viene inizialmente creato e appena sottomesso viene automaticamente approvato o rifiutato in base al budget previsto. Solo in caso di approvazione un utente ha la possibilità di cancellarlo o processarlo.

Utilizzando tale processo realizzeremo una semplice web application per la gestione di una richiesta di acquisto e vedremo come, progettandolo opportunamente, sia possibile rendere il sistema robusto a modifiche minime del processo.

Descrizione dell’Automa

La descrizione del processo può essere fatta mediante l’utilizzo delle API del framework o più semplicemente mediante un XML. Il file XML che rappresenta il workflow purchase order descritto sopra ed utilizzato nel progetto è il seguente:

La Web Application

Prima di entrare nei dettagli implementativi presentiamo le due maschere che compongono l’applicazione e le relative caratteristiche, in modo da rendere chiaro quanto descritto nel seguito. La prima maschera è quella di edit utilizzata per l’inserimento ma anche per la modifica o la semplice visualizzazione dell’ordine:

edit order

La seconda maschera visualizza la lista degli ordini gestiti:

order list

Inizialmente tutti gli ordini sono in stato CREATED (lo stato iniziale del processo). Alla pressione del pulsante Submit è avviata la prima transazione ovvero quella automatica verso gli stati APPROVED o DENIED.  La situazione risultante per i tre ordini sarebbe:

order list after submit

Si nota che l’ordine 1 è i solo approvato in quanto gli altri superano il budget gestito dal processo. L’utente non può più operare sugli altri ordini se non visualizzarne i dettagli. Diversamente per l’ordine 1 può eseguire il cancel o i process. A seguito del processamento l’ordine raggiunge lo stato PROCESSED ed il dettaglio appare come mostrato nell’immagine seguente, dove è anche visibili lo stato attuale e lo storico degli stati attraverso cui è passato l’ordine:

view order 1

Il Progetto

Il progetto di esempio sarà implementato utilizzando Maven e JSF. Per il setup seguiamo quanto già descritto nel post Creare Web App con JSF 2.0. Quindi modifichiamo il pom introducendo la dipendenza al framework Automata:

Creiamo quindi gli oggetti Order e OrderHistory che implementano rispettivamente le interfacce del framework ExtendedWorkItem e HistoryItem. In particolare Order ha la seguente struttura (differente da quella mostrata nell’esempio del framework):

A questo punto introduciamo i due managed bean utilizzati per la web application. Il primo è il WorkflowManagerBean che estende la classe ConfigurableWorkflowManager del framework per eseguire il caricamento dell’XML.

Il secondo bean, OrderManagedBean, implementa la logica necessaria alle due maschere XHTML che visualizzano l’elenco degli ordini e il dettaglio dell’ordine. In particolare nel bean è iniettato il WorkflowManagerBean ed un OrderService che implementa in modo fake i metodi di accesso agli ordini persistiti.

Con riferimento alla maschera che elenca gli ordini mostriamo il codice XHTML che implementa i pulsanti ed i metodi Java presenti nel bean OrderManagedBean attivati dalla loro pressione.

Submit

Il metodo java recupera la descrizione del processo ed esegue il submit. Secondo specifiche tale metodo del framework esegue la prima transizione possibile secondo le condizioni espresse nell’XML. Il processo segue quindi la transizione approve per ordini inferiori a 1000 e deny in caso contrario.

Cancel Order e Process Order

Questi pulsanti non sono codificati nella maschera ma sono il risultato del recupero delle transizioni possibili a partire dallo stato APPROVED. Le label dei pulsanti corrispondono alla descrizione della transazione:

Alla pressione di uno dei due pulsanti viene semplicemente richiesto al workflow di seguire la transazione associata:

Modifica del Processo

Al paragrafo precedente abbiamo visto come sia possibile avere una maschera che mostra dinamicamente i pulsanti in funzione dello stato dell’ordine. Vediamo ora come, modificando il processo, non sia necessario modificare la maschera e la logica già implementata per adattarsi alla nuova “specifica”.

A tale scopo inseriamo una nuova transazione rejected allo stato APPROVED che riporta l’ordine in stato CREATED.

Questa semplice operazione comporterà la presenza di un nuovo pulsante Reject order che sarà visualizzato nella maschera senza la necessità di alcun altro intervento:

reject order

Complichiamo ora l’esempio supponendo di voler consentire il reject solamente per ordini con importo superiori a 500. In questo caso dobbiamo modificare la transition nel modo seguente:

Questo però non è sufficiente perché da specifica il metodo fire() del framework esegue la transizione senza valutare eventuali condizioni. E’ necessario quindi non prospettare all’utente la transizione rejected. Per farlo ancora una volta il framework ci viene in aiuto col metodo evaluateCondition() che utilizziamo nel metodo getTransitions() di OrderManagedBean per filtrare le transizioni consentite:

Ottenendo quindi la situazione mostrata in figura:

order with reject

Conclusione

Sostanzialmente il framework è fortemente orientato alla gestione dello stato di un oggetto di business mediante automi a stati finiti, fornendo tutto il supporto necessario ad automatizzare i cambi di stato e la loro storia. Per semplicità riepiloghiamo nella tabella seguente le caratteristiche principali:

WorkItem, ExtendedWorkItemHistoryItem Sono le interfacce da estendere per implementare gli automatismi che consentono al framework di gestire lo stato degli oggetti e la loro history.
submit() Il metodo valuta le condizioni associate a tutte le transazioni possibili a partire dallo stato corrente ed esegue la prima che restituisce TRUE.
fire() Il metodo esegue la transazione specificata in input ignorando completamente eventuali condizioni associate.
evaluateCondition() Valuta la condizione associata ad una transazione specificata in input.

Codice Sorgente

Il codice sorgente completo per l’esempio descritto è scaricabile qui purchase-order.

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

As you found this post useful...

Follow us on social media!