Quando si parla di stratificazione e layering di applicazioni web, ci si riferisce a quel tipo di situazioni in cui il dominio complessivo è scomposto in livelli (layer appunto) funzionalmente omogenei organizzati secondo una certa gerarchia e tra essi dipendenti. Ciascun layer condivide con gli altri uno strato comune in cui è presente la rappresentazione del dominio di riferimento. In particolare, nelle applicazioni basate sul paradigma ad oggetti come le applicazioni ASP.NET, il processo di scomposizione porta in genere ad identificare un modello ad oggetti (detto anche Object Model) composto da una serie di classi, la cui connotazione può dipendere dalle diverse opzioni offerte delle tecnologie esistenti. Ciascuna classe del modello ad oggetti assolve compiti specifici, può avere responsabilità di fare (ovvero presenta comportamenti espressi sotto forma di metodi), ma soprattutto ha responsabilità di conoscere (ovvero gestisce un suo stato interno tramite campi e proprietà). In uno scenario come quello descritto, le funzionalità principali dell'applicazione vengono rese possibili dalla cooperazione delle sue parti costituenti a minore complessità (SoC, Separation of Concerns) e dalle modalità con cui i dati vengono rappresentati e trasmessi attraverso il sistema.
Figura 1 - Modello ad oggetti in un'applicazione a tre livelli
Come detto, l'output del processo menzionato è un modello ad oggetti che descrive il dominio applicativo e chiaramente durante la scomposizione diventa di fondamentale importanza identificare la forma giusta per creare strutture ad oggetti caratterizzate da una buona estendibilità e flessibilità. Ma quali sono i criteri per creare un modello ad oggetti efficiente? Quali sono gli approcci che influenzano il processo di scomposizione? Quali sono le soluzioni migliori?
In realtà le soluzioni presenti oggi nel .NET Framework e in ASP.NET per la persistenza dei dati nelle applicazioni web condizionano non poco le scelte di disegno e di implementazione del modello ad oggetti. Molto dipende dall'approccio utilizzato e dalle necessità funzionali richieste dall'applicazione. In questo articolo cercheremo di chiarirci le idee su quali scelte operare nelle diverse situazioni, evidenziando di volta in volta le problematiche esistenti e le possibili alternative.
Due diversi approcci
Se si prende in considerazione una qualsiasi applicazione web, in genere essa utilizza un database relazionale per persistere i dati. La presenza di un database rappresenta pertanto una costante nella maggior parte dei casi. Questa situazione influenza non poco il processo di scomposizione, a tal punto che spesso e volentieri la prima azione svolta nella realizzazione di un'applicazione web è la progettazione dello schema relazionale.
La precedenza data alla stesura del database porta ad una scelta di rappresentazione all'interno delle applicazioni in base alla quale le informazioni trattate vengono lette e usate secondo le stesse logiche di rappresentazione presenti nello strato dati. Questo modo di affrontare la definizione del modello ad oggetti prende il nome di approccio Bottom-Up (traduzione: dal basso verso l'alto). Si tratta di un approccio database-driven, dove la progettazione è focalizzata primariamente sul dominio dei dati (database-first).
Se da un lato tale approccio permette uno start-up più rapido, magari agevolato dalla presenza di qualche designer utile allo scopo nel tool di sviluppo, dall'altro esso comporta un forte accoppiamento tra il modello ad oggetti applicativo e lo schema relazionale e introduce un certa rigidità di fondo nella struttura ad oggetti a discapito della riusabilità. Per questo motivo l'approccio Bottom-Up si rivela indicato per applicazioni non complesse, dove gli oggetti in gioco sono in numero limitato e in genere il grafo delle relazioni non è particolarmente, per così dire, intricato.
L'alternativa all'approccio Bottom-Up è il cosiddetto approccio Top-Down (traduzione: dall'alto verso il basso). In tal caso il processo di scomposizione è influenzato primariamente dai requisiti e dagli aspetti caratteristici del dominio in esame e il modello ad oggetti che ne consegue tende a rappresentare i dati secondo le logiche espresse dai casi d'uso dell'applicazione. La forma di rappresentazione delle informazioni trattate non è influenzata dallo schema relazionale, che perde il suo ruolo centrale e diventa un elemento di progettazione come gli altri.
Il Top-Down è un approccio domain-driven e in tal caso l'object model è per lo più composto da classi che intendono fornire meccanismi di rappresentazione ed elaborazione fortemente legati al dominio in questione. Il processo di scomposizione produce un insieme di elementi tra loro relazionati secondo le modalità con cui essi vengono usati nell'ambito dell'applicazione. Il focus quindi si sposta dagli aspetti puramente statici (struttura in tabelle e pura rappresentazione) a quelli dinamici e di interazione (casi d'uso, relazioni, logiche di comunicazione). Il basso accoppiamento con lo strato dati permette una maggiore riusabilità e flessibilità dell'object model, con evidenti vantaggi in termini di manutenibilità. A questo aspetto positivo si contrappone peraltro uno start-up meno rapido, la necessità di dover scrivere più codice e uno scarso supporto di framework e designer automatici. Pertanto questo approccio risulta essere più indicato in scenari più complessi, laddove l'investimento iniziale nella definizione dell'object model viene ripagato da una maggior capacità di far evolvere l'applicazione al variare dei casi d'uso e delle specifiche funzionali.
Come rappresentare i dati
Come si è avuto modo di dire già nel paragrafo precedente, il processo di scomposizione viene fortemente condizionato dalla scelta dell'approccio. Se da un lato il Bottom-Up produce una rappresentazione fortemente dipendente dal database, dall'altro il Top-Down permette un alto grado di flessibilità nella definizione del modello ad oggetti. Pertanto la rappresentazione dei dati nei due casi segue forme diverse a seconda del tipo di soluzione adottata.
Parlando di .NET Framework e ASP.NET 3.5, le scelte possibili per rappresentare i dati sono sostanzialmente due:
- container di dati, ovvero classi come i DataSet tipizzati che permettono di contenere anche grandi moli di dati estrapolati da un database e di mantenerli attivi in memoria affinchè possano essere letti e modificati;
- classi custom (entità), generate tramite i designer di Visual Studio (in particolare quelli relativi a LINQ To SQL ed Entity Framework) oppure definite interamente dallo sviluppatore in modo personalizzato (oggetti POCO - Plain Old CLR Objects).
Di seguito vengono trattate le diverse casistiche in relazione alle possibilità offerte dal .NET Framework nella versione 3.5.
Attenzione: Questo articolo contiene un allegato.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Utilizzare il metodo CountBy di LINQ per semplificare raggruppamenti e i conteggi
Filtering sulle colonne in una QuickGrid di Blazor
Utilizzare la funzione EF.Parameter per forzare la parametrizzazione di una costante con Entity Framework
Persistere la ChatHistory di Semantic Kernel in ASP.NET Core Web API per GPT
Utilizzare EF.Constant per evitare la parametrizzazione di query SQL
Le novità di Entity Framework 8
Change tracking e composition in Entity Framework
Filtrare i dati di una QuickGrid in Blazor con una drop down list
Generare velocemente pagine CRUD in Blazor con QuickGrid
Utilizzare QuickGrid di Blazor con Entity Framework
Ottimizzare il mapping di liste di tipi semplici con Entity Framework Core