Nelle puntate precedenti #
Abbiamo realizzato un semplice modello per la gestione di dati anagrafici e generato la prima versione della nostra applicazione Livebase. Il sistema per ora consiste in una singola vista applicativa (Application) che permette di gestire gli oggetti appartenenti a Employee, l’unica classe presente nel nostro database.
La manutenzione evolutiva #
In questa lezione continuiamo a lavorare sul modello precedente apportando delle piccole modifiche incrementali alla classe Employee: modelleremo informazioni mancanti e miglioreremo la qualità dei dati.
Questo è un ottimo momento per parlare di manutenzione evolutiva: stiamo infatti per compiere un intervento che modificherà il modello di un’applicazione e un database pre-esistenti. In queste situazioni è importante evitare di introdurre inconsistenze nel database o regressioni nell’applicazione in esercizio; fortunatamente, Livebase offre diverse soluzioni per gestire in modo trasparente l’aggiornamento dei modelli senza compromettere l’integrità del database sottostante.
Allinea database e modello #
Come abbiamo visto, il Designer controlla in tempo reale che il modello che disegniamo sia valido e consistente. Quando un modello è installato su una Cloudlet, Livebase controlla inoltre che la struttura del modello – descritta nel Database Schema – sia compatibile con quella del database nella Cloudlet. In caso di incompatibilità, Livebase ci avvisa descrivendo il problema e propone automaticamente, quando possibile, un’azione per risolverlo.
I problemi di allineamento che incontreremo saranno generalmente di tre tipi, classificati in base alla loro gravità:
- Low severity issue: problema che può essere risolto automaticamente da Livebase senza perdita di dati;
- Medium severity issue: problema che può essere risolto automaticamente Da Livebase, ma in alcuni casi comporta la perdita di dati;
- High severity issue: problema di allineamento grave che non può essere risolto automaticamente e che impedisce l’avvio della Cloudlet.
In ogni momento puoi consultare gli errori di compatibilità dal pannello del database di una Cloudlet, a cui puoi accedere cliccando sulla sua icona () dalla Dashboard. Inoltre, questa mostrerà un warning diverso a seconda del tipo di issue, giallo per low severity, arancione per medium e rosso per high.
Gestisci problemi di allineamento #
Proviamo a modificare TutorialEngine per introdurre problemi di allineamento con il database di Workforce.
1. Salva l’engine nella library #
Prima di apportare modifiche a un engine è buona pratica archiviare la versione corrente, specie se – come nel nostro caso – è installato su una Cloudlet con un database non vuoto. Per farlo, trascina l’icona di TutorialEngine e rilasciala sulla Engine Library. Nella finestra di dialogo che si apre puoi inserire una descrizione dell’engine e modificare il commento per la versione, che di default sarà Archived from cloudlet 'Workforce'
.
Non è necessario arrestare la Cloudlet per effettuare l’archiviazione del suo engine.
2. Modifica l’engine #
Prima di modificare l’engine devi arrestare la Cloudlet: dalla Dashboard, clicca su Stop
nel suo riquadro e controlla che il suo stato sia cambiato in Stopped
.
Proviamo a effettuare dei cambiamenti distruttivi: apri TutorialEngine nel Designer ed elimina l’attributo date_of_birth da Employee selezionando l’opzione Delete
dal suo Attribute menu oppure premendo Del.
Apparirà un dialog che avvisa della rimozione, insieme all’attributo, del vincolo di unicità che abbiamo definito per Employee
, in quanto questo include date_of_birth. Clicca su OK
: insieme all’attributo ora è scomparsa l’icona del vincolo di unicità () .
Salva l’engine e chiudi il Designer per tornare alla Dashboard. Lo stato di Workforce è cambiato in Cannot start (engine/database mismatch)
e sull’icona del database è apparso un warning arancione , a segnalare che abbiamo introdotto almeno una medium severity issue. Apri il pannello del Database, e osserva il contenuto della scheda Engine compatibility issues:
Sono presenti due issue relative alla rimozione di elementi dal modello; per entrambe Livebase descrive il problema e l’azione proposta per allineare automaticamente il database. La prima issue è medium severity, in quanto la rimozione della colonna date_of_birth dalla tabella employee comporterebbe l’eliminazione dei valori memorizzati per l’attributo in questione dagli oggetti che abbiamo inserito nel database.
La seconda issue invece è low severity e non ha effetto sugli oggetti del database.
3. Ripristina l’engine #
Abbiamo volutamente commesso un errore; vediamo ora come risolverlo senza perdere i dati sfruttando una versione stabile dell’engine. Per il momento non cliccare su Resolve all issues
. Chiudi invece il pannello Database, fai click destro su di esso, seleziona Delete
e clicca su OK
per confermare l’eliminazione del database. Fatto questo, elimina la copia “rotta” di TutorialEngine presente su Workforce; dopodiché trascina la copia archiviata in libreria e rilasciala sulla Cloudlet. In questo modo, l’engine viene ripristinato e le issue spariscono.
Modifica il modello dati #
Continuiamo a lavorare sulla singola classe Employee e modelliamo altri attributi per tenere traccia di più informazioni sui nostri dipendenti. Apri il Designer cliccando su TutorialEngine e assicurati di trovarti nel Database Schema .
1. Aggiungi nuovi attributi #
Aggiungi degli attributi a Employee cliccando su New attribute
dal Class menu e definendoli come segue:
Attributo | Scopo |
---|---|
email_address: string | L’indirizzo email del dipendente |
position: string | Il suo ruolo in azienda (qualifica o ambito) |
team: string | Il nome del team di cui fa parte |
date_joined: date | La data in cui è stato assunto |
hourly_cost: real | Il suo costo orario |
La classe risultante dovrebbe apparire come in figura. Fai attenzione a scegliere i giusti tipi di dato: date_joined è di tipo date, mentre hourly_cost è di tipo real, poiché vogliamo includere anche cifre decimali. Puoi ordinare la lista semplicemente trascinando e rilasciando i singoli attributi nella posizione che desideri.
2. Restringi il dominio degli attributi #
Al momento è possibile inserire qualunque valore per tutti gli attributi; ad esempio, nulla vieta di inserire un indirizzo email non valido. Tuttavia, possiamo definire delle restrizioni sul dominio dei valori ammissibili per gli attributi.
Per cominciare, cambiamo il dominio dell’attributo position; per i nostri scopi, sarebbe meglio definire una lista di valori predefiniti di ruoli da cui scegliere quello del dipendente da inserire.
Fai click destro su position e, dall’Attribute menu e seleziona Edit domain...
per aprire il Domain Editor per il tipo string.
Abilita la restrizione sul dominio spuntando il flag Value must match any of the following patterns
, e clicca su Add
per aprire lo String pattern editor.
Lascia il campo Pattern type
impostato su Constant
, mentre nel campo Allowed value
digita **Software Developer**
, spunta il flag Match case
(per indicare di tenere in considerazione lettere maiuscole e minuscole) e clicca su OK
. La stringa Software Developer verrà aggiunta alla lista delle stringhe ammissibili.
Aggiungi queste ulteriori stringhe: Project Manager, Security Specialist, System Admin, Designer e Salesperson. Per farlo più rapidamente, dallo String pattern editor seleziona Set of constants
dal campo Pattern type
.
In questo modo potrai scrivere nel campo Allowed value
tutti i valori consentiti, uno per ogni riga. Dopo aver digitato tutti i valori, spunta anche qui Match case
e clicca su OK
.
Dopo aver chiuso il Domain Editor, il Designer ci avverte che questa restrizione del dominio potrebbe generare dei problemi di allineamento per gli oggetti preesistenti; questo pericolo è scongiurato, poiché position è stato appena creato e non è required. Clicca su OK
per confermare le modifiche.
Nota come sia cambiato l’attributo position nella classe, che ora è visualizzato con delle parentesi quadre attorno al tipo: [string]
. Ora imponiamo dei vincoli sull’insieme dei valori ammissibili anche per gli attributi phone_number e email_address:
- Assumiamo che il numero di telefono sia una stringa – perché consentiamo di inserire, oltre ai caratteri numerici, anche simboli come
+@, @-@, @(@ e @)
– e la sua lunghezza massima sia di 20 caratteri. Apri il Domain Editor per phone_number, specifica20
nel campoMax length
, e infine clicca suOK
.
- Per email_address, vogliamo che rispetti il giusto formato e che sia qualcosa del tipo utente@example.com. Certamente non possiamo definire un insieme di costanti, dato che sono ammesse quasi infinite combinazioni. Possiamo invece specificare un pattern codificato in un’espressione regolare. Apri il Domain Editor per email_address, spunta
Value must match any of the following patterns
, e clicca suAdd
. Dallo string pattern editor, selezionaRegular Expression
per il campoPattern Type
, e nello spazio sottostante copia e incolla questa espressione regolare:[^@]+@[^@]+\.[^@]+
.
Per verificare il corretto funzionamento del dominio, inserisci dei valori di prova nel campo Test value, come ad esempio johndoe@thesfor.com
, aaa@bbb
, ccc.org
. Il primo valore viene accettato, mentre gli altri due vengono correttamente rifiutati, in quanto inammissibili. Clicca su OK
e torna al Designer. La classe risultante da queste modifiche dovrebbe apparire come in figura.
3. Definisci un object title composto #
Quando salviamo un modello, il Designer imposta di default il primo attributo di una classe come suo object title, nel caso nessun altro attributo sia stato specificato. Al momento l’object title di Employee è first_name, ed è infatti ciò che appare nel breadcrumb quando esaminiamo un oggetto nell’applicazione; tuttavia, vogliamo che compaiano sia il nome sia il cognome.
Dato che è possibile impostare come object title un solo attributo alla volta, abbiamo un problema che non può essere risolto facendo uso degli attributi “semplici” visti finora; possiamo però sfruttare questa situazione per introdurre gli attributi derivati.
Il valore di un attributo derivato non esiste nel database, ma viene calcolato a livello applicativo a partire dal valore degli altri attributi; il valore calcolato viene mostrato nell’applicazione insieme agli altri attributi - che d’ora in poi, per distinguerli, chiameremo attributi nativi. Possiamo quindi creare un object title sotto forma di attributo derivato per concatenare i valori di first_name e last_name.
Il Designer mette a disposizione una funzione apposita per creare un object title: tenendo premuto Ctrl
, seleziona first_name e last_name e – dall’Attribute menu di uno dei due – seleziona Create object title
per aprire il Math expression editor.
In questo caso, il pannello mostra già un’espressione concat(first_name, " ", last_name)
e riporta in basso Expression validated as STRING
. Ciò vuol dire che l’attributo sarà una stringa contenente la concatenazione del nome e del cognome dell’impiegato, incluso uno spazio per i due valori.
Per ora non preoccupiamoci degli altri aspetti dell’editor; clicca su OK
per confermare e chiuderlo.
Nella classe è comparso un nuovo attributo /title
: il carattere /
, posto davanti al suo nome, indica che l’attributo in questione è derivato. Inoltre è stato già impostato come object title, pertanto appare in grassetto. Poiché contiene il nome completo dell’impiegato, rinomina questo attributo in /full_name
. Nell’applicazione, ad esempio, il breadcrumb per l’impiegato John Doe sarà John Doe
e non più John
.
4. Aggiungi nuovi attributi derivati #
L’object title che abbiamo appena definito è un esempio di attributo calcolato, definito mediante una semplice espressione basata sulla concatenazione di stringhe. Gli attributi calcolati sono uno dei due tipi di attributi derivati che possiamo definire, e sono anche chiamati attributi di tipo math. L’altro tipo sono gli attributi di tipo query, di cui parleremo in seguito.
In generale, l’espressione che definisce un attributo math può essere piuttosto complessa: il Math expression editor consente di definire espressioni, che possono essere calcolate facendo riferimento non solo agli attributi che definiamo sulle classi (inclusi altri attributi derivati), ma anche a ulteriori attributi riservati o di sistema. Per ogni classe sono infatti disponibili tre categorie di attributi riservati, identificati dal prefisso __
:
- Platform attributes: attributi gestiti automaticamente da Livebase; contengono metadati relativi agli oggetti della classe, come l’identificatore univoco (
__id
) e data di creazione (__createdon
). Di default, nel Database schema non vengono mostrati; per abilitare questi attributi su una classe è necessario aprire il Class menu e selezionare l’attributo dalla voceNew platform attribute
; - System properties: attributi che appartengono al sistema nel suo insieme (
__System
), come data e ora correnti (__System.datetime
); - User properties: attributi relativi all’utente corrente della Cloudlet, che ha una sessione aperta e sta lavorando sui dati (
__CurrentUser
); includono informazioni relative al suo profilo (__CurrentUser.username
,__CurrentUser.firstName
,__CurrentUser.email
) e ai suoi permessi (__CurrentUser.isAdmin
,__CurrentUser.team
e altri).
Definiamo degli attributi derivati per Employee: ad esempio, vogliamo distinguere i nuovi arrivati dal resto degli impiegati in base all’età e al tempo trascorso in azienda. Non abbiamo informazioni sull’età dei dipendenti, ma possiamo dedurla a partire dalla data di nascita e definire un attributo derivato age.
Dal Class menu di Employee seleziona New derived attribute
→ Math
per riaprire il Math expression editor, la cui area di testo stavolta appare vuota. Dobbiamo digitare una formula che calcoli la differenza in anni tra la data corrente e la data di nascita; per farlo, abbiamo due alternative: digitarla manualmente oppure selezionare gli attributi dai menu dell’editor. Osserviamoli rapidamente:
- apre la lista degli attributi della classe per cui si sta definendo l’espressione, in questo caso Employee. Include gli altri eventuali attributi derivati e i Platform attributes abilitati;
- apre la lista delle User properties;
- apre la lista delle System properties;
- apre una lista contenente operatori matematici e logici;
- apre una lista di costanti;
- apre il function picker.
Il function picker raccoglie le funzioni che Livebase mette a disposizione per definire espressioni. Selezionando una funzione dalla lista ne viene mostrata la descrizione, che include i parametri richiesti in input con i corrispondenti tipi di dato. Cliccando su Show Examples
puoi consultare esempi di utilizzo di ciascuna funzione.
Nel nostro caso dobbiamo manipolare date: seleziona Date
dal campo Show
e seleziona dateDiff(d1,d2,calendarField)
. Clicca su Insert
per chiudere il Function picker e far apparire la funzione nell’area di testo dell’editor.
Ora dobbiamo sostituire i parametri segnaposto della funzione con gli attributi di cui abbiamo bisogno: sostituisci d1 con la data corrente (__System.date
), selezionando d1
con doppio click e scegliendo date
: date
dalla lista delle System properties. Analogamente, sostituisci d2
con date_of_birth
selezionandolo dalla lista degli attributi. Infine, scrivi field.year
al posto di calendarField
.
Al termine dell’inserimento dovranno essere spariti tutti gli errori, sostituiti dal messaggio Expression has been validated as INTEGER
. Clicca su OK per chiudere l’editor e rinomina il nuovo attributo /math1
in /age
– di tipo integer.
Ora puoi definire – in modo analogo – l’attributo /junior
, di tipo boolean, vero se l’impiegato ha meno di 25 anni e se fa parte dell’azienda da meno di un anno; l’espressione da usare è: age < 25
&& dateDiff(__System.date, date_joined, field.month) < 12
.
Torniamo al Designer e osserviamo le modifiche apportate:
- passando il mouse su un attributo math appare un tooltip con l’espressione che lo definisce;
- selezionando un attributo math, tutti gli attributi della classe che sono usati per definirlo sono cerchiati in rosso;
- selezionando un attributo qualsiasi, tutti gli attributi math definiti a partire da esso sono cerchiati in blu.
Ad esempio, cliccando su /age, date_of_birth è cerchiato in rosso perché compare nell’espressione di /age, mentre /junior è cerchiato in blu, poiché /age compare nell’espressione di /junior.
5. Verifica i cambiamenti #
Verifichiamo ora l’impatto di questo intervento di manutenzione sulla nostra Cloudlet. Per prima cosa, Salva il modello, chiudi il Designer e osserva lo stato di Workforce: sono presenti delle low severity issues che riguardano la presenza di nuove colonne nel database in seguito all’aggiunta dei nuovi attributi. Dal pannello Database puoi notare che le issue riguardano solamente attributi nativi e non derivati, coerentemente con quanto detto in precedenza.
Risolvi automaticamente i problemi di allineamento – ricorda che per le low severity è un passo opzionale! – dopodiché avvia la Cloudlet e accedi al client GraphiQL. Osserva il nuovo schema generato: i servizi e le strutture dati sono ancora presenti, ma sono stati aggiunti nuovi campi alla struttura Employee
, una per ciascun attributo, in modo da poter essere richiesti:
query EmployeePage {
Employee___getPage {
totalCount
items {
_id
first_name
last_name
date_of_birth
phone_number
# nuovi attributi:
email_address
position
team
date_joined
hourly_cost
# attributi derivati:
age
junior
}
}
}
Come puoi notare, sono presenti anche i campi relativi agli attributi derivati Age e Junior; in entrambi sono già stati calcolati dei valori, perché abbiamo inserito una data di nascita per ogni impiegato. Nessuno è junior, non solo perché nessuno ha meno di 25 anni, ma anche perché la seconda condizione dell’espressione è sempre falsa, dato che Date joined è assente per ogni record.
Aggiungiamo un nuovo impiegato junior, usando la mutation Employee___create
. Osserva come sia cambiata l’input data
di tipo EmployeeCreate
: sono presenti nuovi campi per ogni attributo – a eccezione di quelli derivati.
Compila i campi come nella seguente mutation. Se vuoi, puoi verificare il corretto funzionamento delle restrizioni al dominio provando a inserire una posizione non valida, un numero di telefono più lungo di 20 cifre o un indirizzo email non valido: quando proverai as eseguire la richiesta, la risposta mostrerà un errore con un messaggio esplicativo.
mutation EmployeeCreate {
Employee___create(data: {
first_name: "Ted"
last_name: "Hawkins"
date_of_birth: "12/12/2001"
phone_number: "+39 333 1122333"
email_address: "ted.hawkins@thesfor.com"
position: "Software Developer"
date_joined: "01/12/2022"
team: "Thesfor"
hourly_cost: "70.00"
}) {
_id
age
junior
}
}
Esegui la richiesta: come puoi vedere, Ted Hawkins ha 21 anni ed è stato assunto da meno di un anno – supponendo che tu stia leggendo questo tutorial nel 2023 :). Se così non è, prova a creare un nuovo impiegato con diversi valori di Date of birth e Date joined per rendere vera l’espressione junior
.
Modifica la vista applicativa #
Finora abbiamo sempre lavorato sul Database Schema . Vediamo invece cosa possiamo fare per aggiungere funzionalità e personalizzare l’aspetto della nostra vista applicativa. Dalla Dashboard, arresta Workforce e ritorna nel Designer. Stavolta, seleziona Application dalla tab Schemas per aprirne l’Application Schema .
L’interfaccia non è cambiata molto: la classe Employee è ancora presente, ma è colorata in giallo; questo significa che stiamo osservando la classe a livello di Application Schema. In particolare, siamo nella vista applicativa di nome Application.
Nella Panoramica abbiamo introdotto l’Application Schema affermando che consente di partizionare il database. Questo partizionamento avviene modificando la manageability di porzioni del database; in pratica, possiamo decidere quali elementi del database sono gestiti (managed) in ciascuna vista applicativa abilitando o disabilitando classi, attributi nel relativo Application Schema.
In una vista, un attributo non abilitato non compare nelle strutture dati della sua classe definite nello schema GraphQL, mentre una classe non abilitata non compare affatto.
Ad esempio, possiamo abilitare o disabilitare un attributo di Employee cliccando su di essi quando il puntatore riporta l’icona in figura: quelli disabilitati appaiono in grigio. Cliccando sul default class role, invece, puoi disabilitare l’intera classe.
Per ora il nostro modello contiene una sola classe e una sola vista applicativa, perciò non disabilitare nulla prima di procedere; torneremo su questo argomento alla fine del corso.
A questo livello cambiano inoltre il footer e le voci del Class menu e degli altri menu. Passando con il mouse sulla classe appaiono delle icone cliccabili che consentono di accedere a diversi pannelli, ciascuno relativo a un aspetto applicativo per quella classe; è possibile accedere a questi stessi pannelli dal Class menu. Gli aspetti che possono essere modificati sono numerosi, e per comprenderli appieno abbiamo bisogno di un modello più grande. Per ora ci concentriamo sui Class warning.
Imposta dei Class warning #
Nella prima parte della lezione abbiamo imposto dei vincoli sul dominio degli attributi a livello database; questi vincoli sono applicabili solamente agli attributi nativi, e non ad attributi derivati o a platform attributes. Per esempio, dato che /age è un attributo math, non abbiamo modo di controllare dal database che non vengano inseriti impiegati di età inferiore ai vent’anni.
A livello di Application Schema, possiamo comunque sfruttare i Class warning per imporre vincoli su un numero arbitrario di attributi di qualsiasi tipo. Un Class warning è una regola di validazione basata su un’espressione booleana: se si verifica la condizione, si può decidere di impedire l’azione dell’utente oppure mostrare un semplice avvertimento. Nel primo caso si parla di class warning bloccante.
Definiamo dei Class warning Per Employee. Dal Class menu, clicca su Set warnings...
per aprire il Class warnings manager. Clicca su Add
per aprire il Class warning editor
.
Un Class warning è composto da un nome univoco, una condizione e un messaggio che viene mostrato quando la condizione è soddisfatta. L’editor consente di definire la condizione booleana attraverso l’Expression Editor. Inoltre è possibile scegliere in quali situazioni valutare la condizione, spuntando uno o più eventi come in figura, e infine decidere se bloccare o meno l’azione dell’utente.
Definisci i tre warning elencati, aggiungendoli uno per uno cliccando su Add
dal Class warnings manager:
- un impiegato non può avere meno di 20 anni: digita
under20
come nome della condizione,age < 20
come espressione e nel campo del messaggio scriviEmployee's age is under 20!
. SpuntaEvaluated on
→SaveNew
eSaveExisting
: in questo modo la condizione è valutata quando l’utente conferma l’inserimento o la modifica di un record. Infine, spuntaBlock action when message is displayed
per rendere la condizione bloccante; - un impiegato non può essere stato assunto nel futuro: digita
joinedInFuture
come nome,date_joined
>__System.date
come espressione e scrivi il messaggioInvalid assumption date
. Spunta le stesse azioni di sopra e rendi bloccante anche questa condizione; - il salario di un impiegato non deve essere troppo basso: supponiamo che il salario minimo debba essere appropriato rispetto all’anzianità, espressa come numero di anni trascorsi in azienda. Digita
lowCost
come nome e usa l’espressionehourly_cost < (25 + dateDiff(__System.date, date_joined, field.year) * 10);
scrivi il messaggioHourly cost is too low with respect to the employee's experience.
, spunta le stesse azioni ma stavolta non spuntare Block action, in modo da permettere comunque la modifica.
Il Class warnings manager appare ora come in figura:
Clicca su Close
per tornare al Designer e salva il modello.
Il Database Schema non è stato minimamente intaccato dalle modifiche fatte nell’Application Schema. A riprova della separazione di questi due livelli possiamo notare come non sia stato introdotto alcun problema di allineamento. Puoi verificare il corretto funzionamento dei valori di default e dei class warning impostati provando a creare un oggetto con dati non validi (ad esempio, un impiegato con meno di 20 anni o assunto “tra una settimana”).
Conclusioni #
In questa lezione abbiamo modellato attributi calcolati da espressioni (o derivati di tipo math); abbiamo inoltre aumentato la qualità dei dati rappresentabili imponendo vincoli di dominio a livello database e Class warning a livello di vista applicativa. Infine, abbiamo introdotto il concetti di Database compatibility report e di alignment issue relativi all’aggiornamento di un modello in presenza di una struttura del database pre-esistente.
Clicca sul bottone per scaricare il modello che abbiamo realizzato in questa lezione:
Nella prossima lezione… #
Interrompiamo momentaneamente lo studio della modellazione per cominciare una breve digressione sulla gestione dei dati nell’applicazione generata: Importa dati da un foglio Excel.