Il modello non è la realtà
Immaginiamo di partecipare a una riunione per progettare un nuovo sistema software.
Attorno al tavolo ci sono sviluppatori, analisti, esperti del dominio e qualcuno che dovrà prendere delle decisioni architetturali. A un certo punto qualcuno pronuncia una parola.
Customer.
Tutti annuiscono. Sappiamo che cos’è un cliente. Non sembra un concetto particolarmente difficile da comprendere. Un cliente ha un nome, un indirizzo, forse una partita IVA. Può effettuare ordini, ricevere fatture, effettuare pagamenti.
Cominciamo a disegnare il modello. Introduciamo un’entità, le assegniamo un’identità e alcune proprietà. Stabiliamo delle relazioni con altre entità. Dopo qualche tempo abbiamo qualcosa che sembra rappresentare piuttosto bene il problema. Almeno finché qualcuno non fa una domanda apparentemente innocua.
Un cliente che non ha mai acquistato nulla è comunque un cliente?
Dipende. Per il reparto commerciale potrebbe esserlo. Per la contabilità probabilmente no. E una persona che acquista per conto di un’organizzazione? È un cliente? Oppure il cliente è l’organizzazione?
Improvvisamente quella parola, che sembrava così chiara, comincia a perdere la propria solidità.
Non perché abbiamo sbagliato a definirla.
Ma perché abbiamo cominciato a scambiare una nostra rappresentazione per qualcosa che esisteva già, esattamente in quella forma, nel mondo che stavamo cercando di comprendere.
Le cose non arrivano già modellate
Quando progettiamo software compiamo continuamente operazioni di questo tipo. Osserviamo una parte della realtà, riconosciamo alcune regolarità, distinguiamo dei concetti e attribuiamo loro un nome. Poi decidiamo quali caratteristiche conservare.
Un Customer possiede un’identità. Un Order ha uno stato. Un Product ha un prezzo.
Costruiamo relazioni, definiamo vincoli, introduciamo comportamenti. È un processo necessario. Non potremmo costruire software senza ridurre la complessità del mondo a qualcosa che possiamo rappresentare e manipolare.
Ma c’è un aspetto sul quale raramente ci soffermiamo.
La realtà non ci consegna le proprie entità già pronte per essere trasformate in classi, tabelle o Aggregate.
Siamo noi a individuarle. Siamo noi a decidere dove comincia un concetto e dove finisce un altro. E soprattutto siamo noi a stabilire quali aspetti di quel concetto meritino di essere rappresentati.
Non significa che ogni interpretazione sia ugualmente valida. Esistono vincoli, fatti, comportamenti osservabili e conseguenze che possono confermare o smentire le nostre scelte. Significa però che un modello, per quanto accurato, non coincide mai completamente con ciò che rappresenta. Questa distinzione sembra ovvia. Eppure, mentre sviluppiamo software, diventa sorprendentemente facile dimenticarla.
Una domanda vecchia di duemilaquattrocento anni
Nel precedente articolo abbiamo incontrato Parmenide ed Eraclito, due filosofi che, da prospettive differenti, ci hanno costretto a riflettere su ciò che rimane stabile e ciò che cambia.
Platone, vissuto tra il V e il IV secolo a.C., affronta anche il problema del rapporto tra ciò che percepiamo e ciò che possiamo conoscere. Nella sua filosofia troviamo una distinzione fondamentale tra il mondo sensibile, quello che sperimentiamo attraverso i sensi, e il mondo delle Idee, che costituiscono per Platone la realtà intelligibile, stabile e autentica. Le cose particolari che incontriamo sono mutevoli e imperfette. Le Idee, invece, rappresentano ciò che rende possibile comprenderle per quello che sono.
Naturalmente non possiamo trasferire questa concezione direttamente nell’architettura software. Un Customer non è un’Idea platonica. E Platone non stava cercando di risolvere i problemi di modellazione di un sistema informativo. Ma la sua filosofia ci permette di riconoscere una tentazione molto familiare. Quella di considerare il nostro modello come la forma vera e definitiva delle cose.
Abbiamo identificato un concetto, lo abbiamo descritto, abbiamo stabilito quali proprietà lo caratterizzano e quali regole ne governano il comportamento. A quel punto il modello acquista una sorta di autorità. Non ci chiediamo più se Customer rappresenti correttamente il dominio. Cominciamo a ragionare come se il dominio dovesse necessariamente comportarsi secondo la definizione di Customer che abbiamo scritto nel codice. Il problema non è più soltanto tecnico. È diventato un problema di conoscenza.
Le ombre sulla parete
Nel libro VII della Repubblica, Platone racconta una delle allegorie più conosciute della storia della filosofia: il mito della caverna.
Immagina alcuni prigionieri incatenati fin dall’infanzia all’interno di una caverna. Non possono voltarsi. Possono guardare soltanto la parete davanti a loro. Alle loro spalle si trova un fuoco e, tra il fuoco e i prigionieri, passano persone che trasportano diversi oggetti. I prigionieri vedono soltanto le ombre proiettate sulla parete. Non conoscendo altro, finiscono per considerare quelle ombre come la realtà stessa. Uno di loro, però, viene liberato. Può voltarsi, osservare ciò che produce le ombre e, infine, uscire dalla caverna. Quello che scopre mette in discussione tutto ciò che fino a quel momento aveva considerato vero.
L’allegoria riguarda il percorso della conoscenza e il passaggio dalle apparenze alla comprensione. Ma possiamo utilizzarla, con qualche cautela, anche per osservare il nostro lavoro.
Immaginiamo che le ombre siano le informazioni che abbiamo deciso di rappresentare nel software. Gli stati di un ordine. Le proprietà di un cliente. Le categorie di un prodotto. Le regole che descrivono un processo. Non sono necessariamente false. Le ombre della caverna, del resto, non sono un’invenzione dei prigionieri. Qualcosa le produce.
Il problema nasce quando ci dimentichiamo che stiamo osservando una rappresentazione e cominciamo a considerarla l’intera realtà.
Quando la realtà comincia ad adattarsi al modello
C’è però un aspetto ancora più interessante. Le nostre rappresentazioni non rimangono necessariamente separate dalla realtà che cercano di descrivere. Possono modificarla.
Immaginiamo di introdurre in un sistema tre possibili stati per una richiesta:
Pending, Approved, Rejected.
È una classificazione semplice, comprensibile e probabilmente sufficiente per soddisfare i requisiti iniziali. Ma nella realtà una richiesta può essere sospesa, ritirata, approvata con condizioni, rimandata per ulteriori verifiche. Se il software non contempla queste possibilità, le persone devono trovare un modo per lavorare comunque. Forse utilizzano Rejected anche quando intendono soltanto rimandare una decisione. Forse introducono una convenzione informale. Forse utilizzano un campo note per conservare informazioni che il modello non permette di esprimere. Con il tempo queste soluzioni diventano abitudini.
E dopo qualche anno, quando un nuovo architect prova a comprendere il dominio, qualcuno potrebbe spiegargli che una richiesta può essere soltanto Pending, Approved o Rejected. Perché è così che funziona il sistema. E ormai è anche così che le persone hanno imparato a lavorare.
Il modello che avevamo costruito per rappresentare una pratica ha cominciato a condizionare la pratica stessa.
A questo punto distinguere tra il modello e la realtà diventa ancora più difficile. Perché non stiamo più osservando semplicemente una rappresentazione incompleta. Stiamo osservando una realtà che, almeno in parte, si è adattata a quella rappresentazione.
Uscire dalla caverna non significa abbandonare i modelli
Sarebbe sbagliato concludere che dovremmo diffidare di ogni astrazione e rinunciare a modellare il dominio. Non abbiamo alternative. Un sistema software non può contenere tutta la complessità del mondo. Non deve nemmeno provarci.
Un modello è utile proprio perché seleziona, semplifica e rende possibile ragionare su qualcosa che altrimenti sarebbe troppo complesso.
La domanda, allora, non è se il nostro modello sia completo. Non lo sarà mai. La domanda è se siamo ancora consapevoli delle scelte che abbiamo compiuto nel costruirlo.
Perché abbiamo deciso che quella proprietà fosse importante?
Perché due concetti sono stati rappresentati come un’unica entità?
Perché abbiamo introdotto proprio quei confini?
E soprattutto, quali aspetti del dominio abbiamo scelto di non rappresentare?
Non sempre siamo in grado di rispondere. A volte quelle decisioni sono state prese anni prima. A volte erano implicite. Altre volte non ci siamo nemmeno accorti di averle prese. Ma possiamo continuare a metterle in discussione. Possiamo tornare a parlare con le persone che lavorano nel dominio. Osservare ciò che fanno, non soltanto ciò che il sistema consente loro di fare.
Possiamo cercare le eccezioni, le convenzioni informali, i casi che sembrano non trovare posto nel nostro modello. Non per costruire finalmente una rappresentazione perfetta. Ma per verificare se quella che possediamo continua a essere utile.
Le nostre ombre
Forse il rischio più grande per un software architect non è costruire un modello sbagliato. Sbaglieremo. È inevitabile. Il rischio è smettere di riconoscere che quel modello è il risultato di una nostra interpretazione.
Più un’architettura è elegante, coerente e ben strutturata, più può diventare difficile metterla in discussione. I confini sembrano naturali. Le responsabilità sembrano inevitabili. Le regole sembrano appartenere al dominio anziché alla nostra comprensione del dominio. E proprio quando il modello comincia a sembrarci evidente, forse dovremmo diffidarne un po’ di più.
Platone non ci insegna come progettare un Aggregate o dove collocare un Bounded Context. Ma la sua caverna può ricordarci quanto sia facile abituarsi alle proprie rappresentazioni. E quanto sia difficile riconoscerle per ciò che sono.
Non dobbiamo smettere di costruire modelli. Dobbiamo evitare di diventarne prigionieri. Perciò, la prossima volta che osserviamo un diagramma architetturale, una classe o un modello di dominio, forse vale la pena porci una domanda.
Stiamo osservando il problema che volevamo comprendere, oppure soltanto la forma che, a un certo punto, abbiamo deciso di dargli?