[Linq to SQL] Scenari Multi-Tier e SOA

Ispiratori di questo post sono non in ordine:
Una pizza con mozzarella di bufala con il Salta (ma non quella di ieri...quella dell'altra settimana...il che significa che abbiamo un po il vizio ultimamente...e se vedeste come è frequentato bene il locale...verrebbe il vizio a tutti)
Un thread di Guisa di qualche mese fa ormai.
E anche l'argomento Linq to SQL e WCF (...brrrr....).

Con Linq to SQL il nostro Domain Model può discostarsi anche enormemente dalla definizione del database. Si possono creare dei veri e propri grafi da dare in pasto al nostro buon DataContext e dire...tièè...sbrigatela tu.
Prendiamo ad esempio il solito schema di un Ordine e dei suoi dettagli:

image

Ricordiamoci che ci troviamo ora in un mondo Object Oriented, dove a farla da padrone sono proprio le reference/associazioni. Nulla ci vieta infatti (anzi è altamente consigliabile) di avere una associazione bidirezionale tra il parent (Ordine) e i figli (DettaglioOrdine).  Il parent ha una collezione, e ogni elemento della collezione ha una reference al padre.
L'espressività del modello e la potenza di interrogazione introdotta da questa associazione nei due sensi, è molto alta,  permettendo di scrivere query partendo dai child. Chiunque abbia lavorato con un ORM sa di cosa parlo e Linq to SQL non è da meno.

Compito dell'ORM in questione è fornire quante più opzioni possibili (leggasi strategie di fetching) per poter caricare totalmente o in parte il grafo, risparmiare select inutili sulle reference già caricate e così via.

Primo argomenti messo a segno:
1. Potenza del Grafo con associazioni unidirezionali e bidirezionali.
2. La circolarità (abusi a parte) nell'Object Model non è mai stata un problema.

Proiettiamoci nel mondo SOA.
Il nostro applicativo ha un presentation layer, che comunica con uno strato totalmente a servizi.
(Non vedo scenari multi-tier che non si basino su questo presupposto).

Abbiamo la necessità di creare all'interno di un processo, una vista su un Ordine, e in un altro step proporre dei cambiamenti.
Ci servirà un servizio che ci restituisca i dati essenziali dell'ordine al solo scopo di visualizzarli, ed un altro servizio che possa apportare le modifiche.
Domande...
1. Come modelliamo i nostri i contratti di questi due servizi?
2. Possiamo esporre tramite il servizio gli oggetti del Domain Model? Quelli direttamente gestiti da Linq to SQL? (..brrrr..)
3. Linq to SQL...dove sta? Dentro o fuori il servizio?

Facciamoci giudare dai principi SOA e da un po di buon senso.

1. Non si condividono classi, ma contratti. Altrimenti il basso accoppiamento ce lo scordiamo. 
2. Un grafo con reference circolari non può essere serializzato in XML.
3. E anche se si potesse serializzare, ha veramente senso passare dal servizio al presentation tutto il grafo di Ordine?

Analizziamo i due sensi:

Primo Processo
Richiedo una fattura da visualizzare
:

Creiamo un DataContract per il servizio che rappresenta un Ordine light, serializzabile, con una collection di oggetti senza reference al padre, ma soprattutto con dei dati denormalizzati (Magari per ogni articolo, ci portiamo dietro una descrizione, settore e categorie varie, in modo da appiattire il modello).
Dentro il servizio:
1. Usiamo tranquillamente Domain Model e Linq to SQL per caricare il grafo,
2. Utilizziamo un adapter che crei una istanza del DataContract. 
3. Esponiamo il DataContract

Fuori dal servizio non esistono ne Domain Model ne Linq to SQL.
Il presentation Layer non ha bisogno di altro per visualizzare l'ordine.

Secondo Processo
Propongo aggiornamenti sui dettagli dell'ordine, il servizio deve modificare, validare e confermare:

Domandiamoci adesso:
Se anche fosse possibile tecnicamente far viaggiare tutto il grafo...e se si potesse scambiare il grafo tra presentation e servizio...
Ha senso modificare una riga di ordine e far viaggiare per intero un ordine con magari 30 righe di dettaglio?
Per far sapere al servizio che ne è stata cancellata/editata/inserita una sola? 
Magari il tipo di modifica può riguardare solo alcune colonne dei dettagli (mi è capitato sempre) e noi ci portiamo dietro tutto.
Ovviamente non ha senso.

Che si fa? Dentro il servizio:
1. Creiamo un DataContract composto da una List di elementi che rappresentano esclusivamente le modifiche. Ogni elemento potrebbe memorizzare: Tipo Modifica, Quantica etc. Lo scambio dati è fortemente ottimizzato.
2. Quando viene chiamato il servizio, carichiamo il grafo di Ordine e sfruttiamo tutto il lazy possibile e immaginabile. Situazione diametralmente opposta alla precedente. Nel prmo esempio di servizio sappiamo a priori che dobbiamo caricare tutto, qui è tutto da vedersi. Se ci fossero solo inserimenti non dovremmo neanche caricare la collection per intero. Ecco perchè esiste in ogni ORM che si rispetti la possibilità di modificare la strategia di caricamento a runtime per lo stesso medesimo oggetto (LoadOption in Linq to SQL).
3. Snoccioliamo le modifiche sul nostro grafo e persistiamo, magari ci sarà una fase di validazione, notifiche e così via.

Anche in questo caso:
abbiamo sfruttato brillantemente l'object model con tutta la sua complessità all'interno del servizio, e creato un DataContrect leggero. solo con le modifiche. Fuori dal servizio non esiste Domain Model ne Linq to SQL. Internamente si può decidere di cambiare il grafo, magari ci inventiamo qualche altra associazione bidirezinoale (senza esagarare).Ma i contratti rimangono immutati.

Per i puristi SOA, chiedo di non storcere troppo il naso perchè i servizi assomigliano troppo a delle CRUD (definito come CRUDy Antipattern) ma e solo per semplicità di esempio (altrimenti questo post veniva lungo un kilometro).

Questo per me è lo scenario corretto di Linq to SQL in ambienti multi-tier.

Print | posted on martedì 27 novembre 2007 10.22

Feedback

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Raffaeu at 27/11/2007 10.32 Gravatar
A quando il 'Mastering Linq to SQL' ? ;-)

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Giancarlo Sudano at 27/11/2007 10.37 Gravatar
aaaargh...sssshhht...
ovviamente è in preparazione...:-)

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Fabio at 27/11/2007 11.48 Gravatar
Potresti corredare l'articolo con un semplice esempio "compilabile" :)

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Giancarlo Sudano at 27/11/2007 12.01 Gravatar
Fabio, ottima idea...
ma questo post non lo chiamerei articolo...
l'ho scritto stamattina sul tram Gaggiano-Milano...
:-)

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Raffaele Rialdi at 27/11/2007 12.17 Gravatar
Che il DataContract non debba essere il prodotto di Linq, sai che sono più che daccordo.
Ma in certi casi sono anche disposto a rompere il loosely coupled condividendo un assembly nei vari layer se:
- il target è dotnet only
- il lavoro di generazione degli adapter è eccessivo
- se ha senso che quello che estrai vada tal quale al client

Cioè come sempre non farei una generalizzazione della soluzione.

Concordi?

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Tommaso Caldarola at 27/11/2007 13.00 Gravatar
Se provo ad immaginare questo scenario col mio ObjectModel composto da 450 entità... mi vengono i brividi!!! Ovviamente al momento uso un approccio alla Rialdi post.

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Giancarlo Dellamora at 27/11/2007 14.30 Gravatar
Nel caso che il presentation layer fosse un'applicazione WPF, come verrebbero realizzate le classi che espongono i dati all'applicazione (per sfruttare il potenziale del data binding della piattaforma)? Ulteriori classi che implementano INotifyPropertyChanged e che derivino da ObservableCollection<> (se sono liste di oggetti)? Altre soluzioni possibili?

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Raffaele Rialdi at 27/11/2007 15.33 Gravatar
@GD. È proprio quello il problema. Il contratto WSDL è troppo debole per poter rappresentare un object model e credo che anche Janky si riferisse a questo.
Quando dico che in certi casi sono disposto a rinunciare al vero loosley coupled, intendo dire negli scenari dotnet su dotnet è assolutamente pensabile di creare un assembly condiviso tra i vari layer.
BTW la mia RafCollection implementa GetChanges, quindi il discorso di Janky riguardo al marshalling delle sole entities che hanno subito modifiche/inserimenti/cancellazioni è gestito decisamente meglio quando c'è un assembly condiviso (con la RafColletion:) )

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Marco De Sanctis at 28/11/2007 1.40 Gravatar
Solitamente se io sono sia il provider che il consumer, è perché la mia necessità non è quella di esporre servizi, ma voglio semplicemente rendere la mia applicazione distribuita.

In questi casi non vedo controindicazioni a condividere l'object model. Anzi, ne vedo parecchie a *NON FARLO* (classi duplicate, maggiore lavoro di manutenzione...).

Per l'applicazione che stiamo sviluppando in azienda (qualche migliaio di entities) la soluzione del DTO sarebbe completamente impraticabile.

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Raffaele Rialdi at 28/11/2007 8.49 Gravatar
Marco, ovviamente concordo ma bisogna stare molto attenti a non esagerare e ad evitare l'approccio della condivisione se non strettamente necessario.
Il vantaggio dell'approccio SOA va al di là dell'esposizione di un servizio loosely-coupled. Il vero vantaggio all'interno di una soluzione distribuita è di avere il legame più debole possibile tra i vari strati, rendendo versioning, manutenzione e threat model decisamente più semplici.
IMHO vano ben soppesati vantaggi e svantaggi prima di prendere una decisione.

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Alessandro Damiani at 28/11/2007 11.00 Gravatar
Secondo me il vantaggio di un applicazione in architettura SOA è che ci si può tenere la porta aperta verso consumer diversi, magari su piattaforme diverse. Concordo con Raffaele, uno strato di adapter che isola lo strato di Data contract dall'object model vero e proprio a costo dello sforzo iniziale ti ripaga in termini di mantenibilità apportando modifiche e correzioni mantenendo il tutto traparente a tutti i consumer (I confini sono espliciti :))

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Stefano at 28/11/2007 11.31 Gravatar
Complimentissimi a Janky sia per la chiarezza espositiva che per aver mantenuto così chiaro e semplice un concetto secondo me nn banale.. L'ho capito anche io. :D
Francamente, per la mia esperienza, anche io sono per cercare la maggior parte delle volte di avere uno strato di adapter per isolare il mio DOM dal resto, ma alle volte credo che l'approccio "misto" sia giustificabile: "consumo" il servizio senza alcuna trasformazione, praticamente portando l'entity per nn dire DTO (dal data layer) in "sola lettura" direttamente in presentation, e creo e popolo il mio DOM solo per la parte che richiede lavorazione/business logic, in questo caso adapter obbligatori (sto parlando di architettura con fonte dati solo servizi, senza database).

PS: cosa ne pensate di fare un post su GUISA per approfondire le tematiche SOA alla luce delle nuove evoluzioni e prendendo questo post come spunto??

# re: [Linq to SQL] Scenari Multi-Tier e SOA

left by Giancarlo Sudano at 28/11/2007 12.12 Gravatar
@Raf sono d'accordo, in alcuni casi ci si possono fare degli sconti, nel mio esempio parto dal presupposto che si è già scelto (SOA)di alzare una barriera tra consumer e provider.
I due commenti di Marco e Tommaso sono la testimonanza di progetti grossi (mille entità), dove i DataContract possono portare notevole entropia, ma i problemi di quei progetti forti sono altri. Tu hai detto la parola magica "versioning", ma anche "threat model".
I componenti trasversali fortemente accoppiati uccidono i cicli di vita delle applicazioni, tempestano clamorosamente i repository di codice, levano la salute degli sviluppatori.
Un approccio SOA corregge questo, e il vantaggio nn lo si vede sulla singola applicazione ma sull'applicazione dei principi su tutto un IT.
Comments have been closed on this topic.