Prendo spunto da un post di Mario per allargare la discussione e dire la mia in un contesto più comodo, i commenti purtroppo non sono comodi e non abbiamo più un forum, speriamo che torni presto perchè sarebbe stato il posto ideale.

Disclaimer:

Quando parliamo di architettura ci possiamo mettere su due livelli:

  1. Architettura fine a se stessa: inteso come la soluzione ideale in un mondo ideale;
  2. Architettura calata nel contesto: la soluzione ideale con tutti i constraint del caso derivanti dal mondo che sto vivendo in quel momento, tipicamente le esigenze del cliente;

Questo post vuole solo ed esclusivamente fermarsi al punto 1. Ha senso fermarsi al punto 1? si, perchè ad esempio il punto 2 non esisterebbe senza il punto 1 poi potremmo anche scoprire che neanche il punto 2 ha senso perchè non ha senso parlare di architettura… ma non è oggetto di questo post.

Partendo dalla notte dei tempi ci hanno insegnato che un modello possibile è sintetizzabile così:

image

Questo modello si può evolvere ed arricchire fino ad arrivare a qualcosa di simile al concetto di onion, ma non è questo l’argomento; quel modello alla fine della fiera ci permette di scrivere un qualcosa del tipo:

Person aPerson = myPersonRepository.GetById( “mauro” );

Questo probabilmente produce una chiamata verso un db con un comando sql, i dati tronano indietro viene istanziata una Person e i dati vengono “spinti” nell’istanza di Person appena creata.
Quali sono i limiti di questo approccio? è decisamente rigido. A fronte della necessità di “cercare” istanze di Person con criteri diversi bisogna estendere il DAL/BusinessLayer aggiungendo le funzionalità del caso. Ecco che ci si comincia a chiedere se un linguaggio di query ad alto livello non abbia senso in questo contesto e la risposta che ci diamo è quasi certamente si.

Possiamo quindi evolvere il nostro modello introducendo qualcosa che ci aiuti, e che nel mondo reale probabilmente ci salva la vita:

image

e produrre un codice del tipo:

IDataContext session = //Bla bla…
Person aPerson = session.Get<Person>( “queryDefinition” );

l’esempio è triviale, in pseudo codice e potrebbe essere adattato a molti O/RM (o simili…) ma ancora una volta non è questo l’argomento, come non lo è il concetto di transazione di business ne tanto meno quello di data context.
Quello che invece mi preme far notare è che nel nostro modello abbiamo introdotto qualcosa che abbiamo definito infrastruttura e che alla fine della fiera è trasversale anch’essa all’applicazione introducendo una dipendenza.
n.d.r. Non è detto che questo debba essere visto come un problema nel mondo reale, anzi.

Se cominciamo a ragionare ci rendiamo conto che quella dipendenza, nel mondo reale è decisamente difficile da rimuovere, l’esempio lampante è NSK, in NSK Andrea, non è una critica ma una semplice constatazione, espone dal DataContext un metodo GetByCriteria( Query q ) che ha la velleità di astrarre il concetto di query ma se andiamo a guardare l’implementazione o pensiamo ad una possibile implementazione ci rendiamo subito conto che l’obiettivo è decisamente difficile da raggiungere, l’espressività del linguaggio T-SQL, ad esempio, è decisamenta alta e modellare un dominio che lo rappresenti è decisamente difficile.

Quello a cui giungiamo quindi è che la nostra creatura consolida la sua dipendenza dall’infrastruttura, e ripeto non è detto che sia un male.
Ci potremmo però trovare in una spiacevole situazione, esperienza proprio di questi giorni:

La topologia di rete dei client che usano la nostra applicazione cambia/evolve, rendendo necessario uno switch da multi-layer a multi-tier perchè alcuni client adesso sono remoti, di mezzo c’è un “malefico” firewall e da li ci passa solo https443.

image

L’infrastruttura smette di funzionare, semplicemente perchè non è pensata per funzionare in quello scenario, ne consegue che l’applicazione smette di funzionare perchè dipende dall’infrastruttura, quello cha abbiamo adesso è un nuovo modello concettuale:

image

in cui purtroppo non possiamo più produrre del codice come quello che abbiamo visto in precedenza se non astraendo il concetto di query, di data context, di transazione di business etc etc.

E’ possibile farlo? certo che è possibile;
Come? molto semplice Expression Tree;

Sarebbe molto bello poter scrivere una qualcosa del tipo:

IServiceContainer sc = ServiceContainer.GetContainer();
using( IDataContext dc = sc.GetService<IDataContext>() )
{
    var query = dc.Persons.Where( p => p.FirstName == “Mauro” );
    IEntityCollection<IPerson> list = query.ToEntityCollection( dc );
    //Operate on list…
    dc.Commit();
}

E’ fattibile? certo che si! quello snippet compila e funziona pure… producendo questo sql:

select [_t0].[id], [_t0].[firstName], [_t0].[lastName] from [Persons] as [_t0] where [_t0].[firstName] = @p1

Cosa è costato? un bagno di sangue :-D oltre al dettaglio che quello funziona mentre questo neanche a morire:

var query = from person in dc.Persons where person.FirstName == “Mauro” select person;

…nonostante, per lo sviluppatore che l’ha scritto, siano esattamente la stessa cosa… solo per lui purtroppo :-D, ma anche questa è un’altra storia.

Quello a cui voglio arrivare è che dal punto di vista della modellazione/astrazione del linguaggio di query adesso (.NET >=3.x) abbiamo quasi tutto, le Lambda Expression sono in questo senso la soluzione ad alto livello, il problema adesso nasce quando si cerca di calare questa bellissima possibilità in un contesto reale e si (ri)scopre che scrivere un query provider per Linq è molto lontano dall’essere un’operazione anche solo pensabile…

Ma, alla fine dei conti, serve tutto ciò? non è detto, ripeto dipende dal contesto. Se ci caliamo nella realtà in cui stiamo lavorando potremmo anche arrivare a scoprire, estremizzando nella direzione opposta, che nulla di tutto ciò ha minimamente senso, potremmo arrivare a trovarci di fronte ad applicazioni scritte male, potenzialmente immanutenibili, e intestabili, ma che funzionano e lo fanno bene… e non è fantasia, ho sotto gli occhi due esempi realissimi proprio in questi giorni ;-)

.m