Software

FP: Perché sviluppare un nuovo framework

Consigli e best practices

Daniele Malgeri, Fabio Pasqua Aprile, 2020
FP: Perché sviluppare un nuovo framework

Apri Google. Cerca “Java ORM Frameworks”. Il primo risultato è un elenco non esaustivo dell’enorme quantità di framework ORM disponibili.

Leggi un po’ di opinioni. Scegline uno, studialo, provalo, e comincia ad utilizzarlo nei tuoi progetti. Ce ne sono così tanti che sicuramente troverai quello giusto per le tue esigenze. Ci sono così tanti sviluppatori, là fuori, che la soluzione al tuo problema sarà già stata trovata ed implementata da qualcuno.

Allora, perché scrivere un nuovo framework? Perché alzi la mano, tra gli sviluppatori che ci leggono, chi non ha mai pensato almeno una volta di fronte all’ennesimo workaround: "Adesso lo smonto e me lo riscrivo come dico io".

The good thing about reinventing the wheel is that you can get a round one

Nasce così FP, da un programmatore che ha scelto la strada più sfidante per soddisfare le proprie esigenze. Certo, ci vogliono anche le condizioni adatte: non è così scontato avere l’occasione giusta di potersi cimentare in un progetto di questo tipo, ma il bello di lavorare in un’azienda giovane e in costante crescita è anche questo.

FP è ispirato ad un altro framework, ideato e sviluppato per PHP da alcuni colleghi con i quali condividiamo la stessa filosofia. Il suo nome è Boostack.

Boostack e FP condividono la medesima idea di partenza: due classi astratte, BaseClass (in Java BasePojo) e BaseList. Queste due verranno estese da tutte le classi che faranno parte del DAL.

L’idea di fondo è semplice: consentire ai DAO di avere già a disposizione i metodi select, insert, update e delete semplicemente estendendo una classe astratta.

Come si usa

Talk is cheap. Show me the code

Vediamo un esempio di come può essere usato FP in un semplice DAO.

Ipotizziamo di avere una tabella nel nostro DB (qualsiasi SQL) così strutturata:

TABLE 'T_DAO_EXAMPLE'(
  'ID' int(11) NOT NULL AUTO_INCREMENT,
  'FIELD_ONE' varchar(45) DEFAULT NULL,
  'FIELD_TWO' int(11) DEFAULT NULL,
  'FIELD_THREE' tinyint(4) DEFAULT NULL,
  'FIELD_FOUR' varchar(45) DEFAULT NULL,
  PRIMARY KEY ('ID')
);

L’utilizzo è molto intuitivo, è sufficiente estendere la classe BasePojo, utilizzare le annotazioni nei punti giusti e il gioco è fatto. Qui sotto è possibile vedere il primo esempio. Tutte le operazioni CRUD sono già disponibili con queste dieci righe di codice.
 

@FPTable(tableName = "T_DAO_EXAMPLE")
public class DaoExample extends BasePojo {

    String fieldOne;
    Integer fieldTwo;
    boolean fieldThree;

    @FPColumn(name = "FIELD_FOUR")
    String customNameField;

    @FPExcluded
    Object appOnlyField;
}

Com’è possibile notare, FP è, come la maggior parte dei framework Java, annotation driven. Non è sempre stato così: le sue versioni più giovani erano configurabili attraverso una serie di variabili statiche. Una volta iniziato ad utilizzarlo nei progetti più complessi è stato deciso di sfruttare l’enorme potenziale delle annotazioni, uniformando così il suo funzionamento a quello dei framework più blasonati.

A fini dimostrativi sono state utilizzate le principali annotazioni di FP, ma nel caso più semplice l’unica obbligatoria è @FPTable. Vediamo una breve descrizione di queste annotazioni:

  • @FPTable è dove vengono inserite le principali informazioni riguardanti la tabella nel DB, prima fra tutte il nome.
  • @FPColum permette di rinominare in modo personalizzato uno dei campi del DAO, sovrascrivendo la nomenclatura di default dell’oggetto.
  • @FPExcluded è utile quando abbiamo qualche campo necessario nell’applicativo ma che non vogliamo includere nel DB.

Riguardando la tabella SQL c’è una colonna in più: l’ID. Ogni DAO che estende BasePojo eredita questo intero auto incrementale, utilizzato per identificare univocamente le entry nel DB. È una restrizione voluta, in quanto reputiamo una best practice inserire sempre un id intero auto incrementale nelle tabelle. Tuttavia, nulla vieta in futuro di rivedere questa funzionalità, consentendo di utilizzare altre primary key diverse da quella di default.

Una volta modellato l’oggetto non resta che provare le CRUD messe a disposizione da FP.
 

DaoExample daoExample = new DaoExample();
daoExample.fieldOne = "first value";
daoExample.fieldTwo = 2;
daoExample.fieldThree = true;
daoExample.customNameField = "custom field";

// CREATE (ID isn't present in the object)
int id = daoExample.save();

// READ
// SELECT * FROM T_DAO_EXAMPLE WHERE ID = #{id}
daoExample.select("ID", id);
// SELECT * FROM T_DAO_EXAMPLE WHERE ID = #{id} AND FIELD_ONE = 'first value'
daoExample.select(new WhereParams("ID", id), new WhereParams("FIELD_ONE", daoExample.fieldOne));

// UPDATE (ID is present in the object)
// UPDATE T_DAO_EXAMPLE 
// SET (FIELD_ONE = 'second value', FIELD_THREE = 'True', FIELD_FOUR = 'custom field') 
// WHERE ID = #{id}
daoExample.fieldOne = "second value";
daoExample.save();

// DELETE
// DELETE FROM T_DAO_EXAMPLE WHERE id = #{id}
daoExample.delete();

La SELECT può essere effettuata sia filtrando per ID che utilizzando altri criteri, utilizzando un costrutto messo a disposizione dal framework: il WhereParams.

Questo oggetto consente di specificare:

  • Colonna
  • Operatore
  • Valore da confrontare
  • Congiunzione AND (default) oppure OR

Finora abbiamo visto come recuperare una singola entry, ma come fare con una lista? In questo caso usufruiremo della BaseList, un’implementazione dell’interfaccia List di Java.

L’utilizzo non è dissimile da BasePojo con l’aggiunta di quelle funzionalità comode per la manipolazione di liste di dati come: paginazione, ordinamento e intervallo.

Vediamo un esempio:
 

BaseList<DaoExample> daos = new BaseList<>(DaoExample.class);
//START CREATE QUERY
// SELECT * FROM T_DAO_EXAMPLE WHERE ID = 1 OR ID = 2 ORDER BY ID DESC
daos
    //WHERE PARAMS
    .where(new WhereParams("ID", 1, DBConjunction.OR), new WhereParams("ID", 2))
    //PAGINATION (optional)
    .page(1, 50)
    //SORTING (optional)
    .orderBy(DBSort.DESC, "ID")
    //QUERY DB
.doSelect();

La classe passata al costruttore consente a BaseList di poter lavorare con le nuove istanze di BasePojo necessarie, in quanto Java non consente nuove istanze di tipi generici.

Java is C++ without the guns, knives, and clubs

L’esempio visto rappresenta l’utilizzo classico e più comune della BaseList, ma non è l’unico. Al programmatore sono anche messi a disposizione query con cursori e costrutti adatti quando si deve recuperare dati in maniera asincrona.

E le query più complesse?

FP è nato con l’intenzione di rendere facile e immediato eseguire le query più semplici. Tuttavia, sappiamo bene che spesso non sono sufficienti.

In questo momento FP si basa su MyBatis, che è stato scelto proprio per la sua ottima gestione dei mapper nel caso in cui servisse scrivere query più complesse. Nel caso in cui fosse necessario, è quindi possibile con FP utilizzare i mapper di MyBatis, in maniera identica a quella di quest’ultimo ORM. Di fatto, con l’introduzione dei WhereParams, le uniche query che necessitano dell’uso dei mapper sono quelle che richiedono l’uso delle join.

Oltre ai mapper, FP sfrutta il motore di MyBatis anche per la gestione delle connessioni al DB e dei Driver.

La prova sul campo

FP è utilizzato con successo su diversi progetti enterprise, ed è ormai diventato un’importante risorsa aziendale. È costantemente aggiornato e mantenuto, e numerose nuove features (e ogni tanto, perché no, anche qualche bug) sono state introdotte nel corso del tempo. Così FP non è più un semplice ORM: il "core" è stato isolato, e package di utility e funzionalità che sfruttano il core sono importabili al bisogno.

FP ad oggi è in grado di gestire anagrafiche, sessioni, log, permessi e privilegi.

Il team di sviluppo ha un sogno nel cassetto: aggiornare il core e consentire la gestione di semplici join senza dover ricorrere ai mapper. Per ora ci dobbiamo accontentare…

Ne è valsa la pena?

The beautiful thing about learning is nobody can take it away from you

Il grande valore aggiunto di FP non è soltanto la quantità di codice riusabile che sta producendo, ma anche il bagaglio di esperienza che il team di sviluppo porterà sempre con sé nei nuovi progetti. Scrivere un framework ti costringe a cambiare prospettiva: l’utente non è più chi schiaccia i bottoni sul front end, ma è un altro programmatore come te. Siamo fermamente convinti che qualsiasi sviluppatore con sufficiente esperienza dovrebbe provare a scriverne uno, costringendosi ad affrontare tematiche che solitamente affronta solo nei panni dell’utente.

Per gli sviluppatori più junior, invece, il consiglio è quello di non limitarsi mai a lavorare in “black-box”. Qualsiasi metodo richiamato semplicemente per ottenere un output, senza chiedersi come possa funzionare, è un’occasione persa per imparare, e magari un giorno "smontarlo e riscriverlo come dico io".

Un'ultima curiosità

Quasi tutti, al primo approccio con FP, si chiedono la stessa cosa: “Cosa significa l’acronimo?”.

Il significato ufficiale dell’acronimo è “Fast and Productive”, ma ce n’è un altro. Per conoscerlo (e soprattutto comprenderlo), i ragazzi del team di sviluppo consigliano almeno due o tre mesi di esperienza lavorativa in Certimeter Group.

Articoli correlati

Software

Articoli in evidenza

Approfondimenti

UNISCITI A NOI. INVIA LA TUA CANDIDATURA

Certimeter Group crede nei valori, nella passione e nella professionalità delle persone.