Una decina d’anni fa, quando Angular 1 era da poco stato pubblicato, suscitando non poco interesse nella comunità degli sviluppatori, Node.js non si era ancora affermato e il supporto Oracle per il formato JSON era di là da venire.
Il nostro team aveva già maturato diverse esperienze di sviluppo applicazioni utilizzando il linguaggio PL/SQL e stava rapidamente prendendo confidenza con i paradigmi di programmazione tipici di Angular.
Ben presto emerse quindi l’esigenza di capire come integrare le due cose per sfruttare al meglio le conoscenze acquisite. Inoltre non volevamo usare Oracle come document store (allora il supporto di tale utilizzo era pressoché inesistente, e usavamo già MongoDB o DynamoDB per quell’esigenza) bensì ci interessava lo sviluppo di Single Page Applications con un RDBMS tradizionale.
Dopo alcune ricerche e sperimentazioni scoprimmo la libreria PL/JSON, che consente di leggere e scrivere oggetti JSON direttamente in PL/SQL. Si trattava della chiave di volta dell’integrazione che volevamo realizzare.
Iniziò quindi a prendere forma l’idea di implementare la business logic all’interno di packages PL/SQL richiamabili direttamente dall’app Angular. Idealmente l’app avrebbe dovuto eseguire le classiche chiamate REST (GET, POST, PUT e DELETE), specificando il nome del package e passando (opzionalmente) dei dati in formato JSON, ricevendo una risposta nello stesso formato.
Naturalmente desideravamo una soluzione generica, che potesse essere utilizzata in progetti diversi, idealmente senza modifiche “architetturali”.
Nacque così il concetto di PL/SQL Gateway, cioè uno strato software con componenti in tutti e tre i livelli architetturali:
- Lato client un modulo che implementa le chiamate REST al server
- Lato server uno strato software incaricato di ricevere tali richieste e instradarle al gateway package su Oracle (all’epoca in Java, successivamente re-implementato come modulo Node JS utilizzando la libreria node-oracledb)
- In Oracle un package incaricato di ricevere tutte le chiamate e invocare dinamicamente il package e il metodo desiderato
La soluzione offre a nostro avviso diversi vantaggi:
- Tutte le interazioni con i dati avvengono in PL/SQL, eliminando la necessità di scrivere istruzioni SQL nel middleware o di usare qualche tipo di ORM
- Chi sviluppa la parte di Fontend non deve preoccuparsi di sapere come sono strutturati i dati, è sufficiente che sappia quali sono i packages da richiamare e la struttura del JSON in cui i dati devono essere trasmessi
- Chi sviluppa la parte di back-end lavora in un ambiente a lui familiare (codice PL/SQL che risiede nel db) completamente agnostico rispetto agli strumenti usati per sviluppare l’interfaccia
Un esempio pratico
Per illustrare come mettiamo in pratica questo approccio, ecco alcuni estratti da uno dei nostri progetti.
Componente middleware (oracle-plsql.js)
Come si vede vengono esposti due metodi di utilità (initialize e shutdown - per aprire e chiudere la connessione al db), due metodi di lettura (read e readAll - per leggere un singolo record o un set di risultati), tre metodi di scrittura (create, update e delete) e un metodo generico (method - per invocare un package e una procedura specifici).
PkgGateway è il nome del package che riceverà tutte le richieste.
A fronte di tali interfacce verso il db, queste sono tutte le rotte Express che servono:
Il middleware authz (qui non illustrato in quanto esula dall’oggetto dell’articolo) ha il compito di verificare che l’utente sia autenticato e abbia i privilegi necessari.
La funzione getCtx crea un oggetto di contesto che verrà inviato al db insieme ai dati specifici della chiamata e ha lo scopo di comunicare ai packages PL/SQL l’utente che ha eseguito la chiamata.
Componente Oracle (package gateway)
Questo package controlla la validità dell’utente che ha fatto la chiamata (procedure read_context), quindi chiama dinamicamente (execute immediate) il metodo opportuno del package richiesto, passando i dati necessari e ricevendo la risposta (in formato JSON) in una variabile clob che viene restituita a Node.
Client Plugin (plsql.js)
Infine mostriamo un esempio di componente lato client. L’implementazione ovviamente sarà diversa in base al framework di riferimento, quella riportata è relativa a Vue 2.
Dopo aver incluso il plugin nel main.js
Sarà possibile invocare i metodi dai files .vue. Ad es.:
Chiudiamo con un esempio di implementazione del package:
Quanto fin qui esposto non costituisce certo un’illustrazione completa di tutto il ciclo di sviluppo: l’obiettivo è delineare il nostro approccio allo sviluppo di moderne applicazioni web basate su database Oracle.