martedì 14 febbraio 2012

Domain Model o View Model?

Per quanti di voi hanno studiato e utilizzano ASP.NET MVC o comunque qualsiasi altro framework basato sul pattern MVC, saprà benissimo cosa si intende per Model, View e Controller.
Bene, se per view e controller generalmente non si hanno dubbi nell'implementazione pratica, per il model è possibile farsi qualche domanda in più.

Devo usare le mie classi di dominio? In molti libri di ASP.NET MVC o quasi in tutti gli esempi in giro per la rete, troverete la solita classe Product o User che viene utilizzata nel controller e passata tranquillamente alla view. Niente di più facile, no?

Ma sarà sempre così? Beh la risposta è...quasi mai!
Basta passare dall'esempio alla prima pagina "reale", per capire subito che le classi da utilizzare nella view non possono essere quelle di dominio. Ecco alcune motivazioni:

  • E' quasi matematicamente certo che una pagina web non contenga dati di una solo classe di dominio. Banalmente se stai creando la pagina di edit del tuo caro oggetto Product e una delle sue proprietà deve essere selezionata da una tendina, dove passo alla view gli item dell'elenco?
  • Può capitare che esistano delle regole di validazione diverse a seconda della view in questione. Queste dunque non possono essere definite tramite il solo meccanismo di validazione tramite attributi delle classi di dominio
  • Capiterà facilmente che la view dovrà gestire solo alcune delle proprietà della classe di dominio. Perchè dunque dovrei fare il bind dei dati con l'intera classe Product, andando magari incontro a problemi di sicurezza nel caso ci fosserò proprietà che non possono essere editati dall'utente? Un utente malintenzionato potrebbe infatti iniettarvi anche queste proprietà in post. E' vero che si può sempre definire a livello di action quali proprietà far gestire o meno dal binder, ma è necessaria una configurazione in più e sopratutto ricordarsi di farla:)

In conclusione, difficilmente le vostre classi di dominio potranno essere passate direttamente alla view, bensì nella quasi totalità dei casi sarà necessario crearsi delle classi ad-hoc (View Model) con le sole informazione che interessano alla view e che vengono poi gestite da essa stessa.

E' innegabile che ciò è un'attività dispendiosa in tempo, ma esistono alcuni progetti interessanti che possono venire in aiuto, come AutoMapper (http://automapper.org) che ti permette di mappare automaticamente le classi del View Model con quelle del Domain model.


venerdì 10 febbraio 2012

L'utilizzo della cache in Nhibernate

Una grande funzionalità che possiede Nhibernate è quella della possibilità di gestire e personalizzare l'utilizzo della cache.
Per differenziarsi dalla cache che nhibernate utlizza nativamente (cache di primo livello), quella, la cui gestione è delegata a noi utilizzatori, è chiamata cache di secondo livello.

Il suo utilizzo è davvero semplice.

Ammettiamo di avere una tabella di configurazione nel nostro database (t_configuration), e di avere la relativa classe mappata (ConfigurationEntry)

E' naturale che noi vorremmo cachare il contenuto di tale tabella, poichè probabilmente verrà modificata raramente e, allo stesso tempo,utilizzata di frequente.
Quello che è necesario fare è aggiungere al file di mapping della classe il seguente tag:

<cache 
    usage="read-write|nonstrict-read-write|read-only"                
    region="RegionName"                                              
/>

usage
read-only: nel caso i dati vengano sempre solo letti (è il nostro caso)
read-write: nel caso i dati possano essere sia letti che scritti (controllare che il provider di caching scelto abbia il supporto al locking)
nonstrict-read-write: nel caso i dati possano essere letti e raramente scritti. Dunque probabilmente è possibile trascurare casi di transazioni simultanee

region
RegionName: nome della regione di cache (di default il nome della classe)

Dunque nel nostro caso avremo un file di mapping simile al seguente:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="Test" assembly="Test" 
xmlns="urn:nhibernate-mapping-2.2">
<class name="ConfigurationEntry" table="t_configuration" schema="dbo">
       <cache usage="read-only"/>
       <id name="Key" access="property" column="Key">
         <generator class="assigned" />
       </id>
       <property name="Value" type="String" column="Value" length="50" />
</class>
</hibernate-mapping>
 
A questo punto se proviamo a eseguire per due volte consecutive la seguente funzione passandogli lo stesso parametro key

public string GetValueByKey(string key)
{ 
   return Session.Get<ConfigurationEntry>(key);
}

potremmo notare che solo la prima volta viene eseguito l'accesso al database, mentre la seconda volta il valore viene prelevato direttamente dalla cache.

Attenzione però che tale cache viene utilizzata solo per restituire le entità a partire dalla loro chiave. Nhibernate infatti ha messo in cache la coppia chiave-classe. Ma cosa accade quindi se io lancio per due volte consecutive la seguente funzione che mi restituisce tutti i record di configurazione?


public List<ConfigEntry> GetAllConfigurationEntries()
{
   return Session.CreateCriteria<ConfigurationEntry>()
.List<ConfigurationEntry>().ToList();
}

Ebbene l'accesso al database viene effettuato in entrambi i casi. Questo perchè Nhibernate non ha messo in cache la query ma solo le coppie chiave-classe.

Per tale motivo ci viene in aiuto la cosidetta Query Cache, che permette a Nhibernate di mettere in cache il risultato della query. La query cache viene attivata nel seguente modo:

public List<ConfigEntry> GetAllConfigurationEntries()
{
   return Session.CreateCriteria<ConfigurationEntry>().SetCacheable(true)
           .List<ConfigurationEntry>().ToList();
}


Ora eseguendo due volte la funzione, viene effettuato un solo accesso al database.
Attenzione che la query cache mette in cache solo le chiavi del risultato della query. E' necessario dunque abilitare anche la cache di secondo livello alla classe in questione, come abbiamo visto prima.


martedì 7 febbraio 2012

Localizzazione e SEO di un sito multilingua


Per chi di voi si è imbattuto nello sviluppo di un sito internet multilingua, probabilmente avrà capito che la gestione di più lingue non è un tema così facile da smarcare come potrebbe sembrare superficialmente.
Se poi ci aggiungiamo il fatto che il sito da sviluppare è per il pubblico e la sua popolarità ne determina il successo, il tutto diviene più delicato e meritevole di riflessioni più approfondite.

Riporto dunque qui le considerazioni da tenere a mente, con particolare attenzione alle problematiche SEO. Dobbiamo, infatti, avere la garanzia che il nostro sito sia indicizzato in TUTTE le lingue.

  • Evitare che il riconoscimento della lingua da utilizzare avvenga esclusivamente tramite la lettura della lingua del browser dell'utente, oppure tramite la localizzazione geografica dell'utente in base al suo IP. Tale approccio è totalmente letale ai fini del SEO. Ricordiamoci infatti che il crawler di Google (e ovviamente anche degli altri motori di ricerca) si presenta al nostro sito con un IP e una lingua imprevedibile (nel maggiore dei casi US). Per i motori di ricerca esisterebbe perciò solo la versione statunitense del nostro sito.

  • Per risolvere tale problema è necessario permettere al crawler di "cambiare lingua". Ciò può essere fatto prevedendo la presenza di link alle varie versioni localizzate del nostro sito. La presenza delle solite bandierine nazionali o di semplici link alle lingue disponibili, può essere una soluzione.

  • Garantire un'associazione uno a uno tra url e pagina localizzata. Come già detto dunque, evitare ragionamenti su cookie, lingua del browser, IP ecc.. tutti elementi estranei ai motori di ricerca. Questi strumenti vanno benissimo come "aiuti" da utilizzare al fine di reindirizzare automaticamente l'utente alla versione del sito con la sua lingua.

  • Evitare il problema di duplicazione del contenuto tanto odiato dai motori di ricerca (due pagine diverse con lo stesso contenuto). Per tale motivo è necessario capire se si avrà a che fare con un sito che avrà contenuti diversi per ogni country (sito localizzato) oppure se il sito conterrà esclusivamente contenuti tradotti nelle varie lingue disponibili (sito tradotto).
    Tale differenziazione è fondamentale. Se infatti il vostro è il secondo caso, è necessario creare url che si differenziano solo per la lingua e non per la country. Ad esempio evitare pagine come www.tuosito.com/en/pagina.aspx e www.tuosito.com/en-US/pagina.aspx.
    In questo caso infatti le pagine sarebbero identiche (entrambe in inglese) e rompereste la relazione univoca tra url e contenuto della pagina.
    Se questo è il vostro caso, la tendina delle bandiere nazionali deve essere sostituita con la scelta delle sole lingue disponibili.

  • Qual è l'organizzazione migliore per il mio sito multilingua? In pratica ci sono tre possibilità, ognuna che presenta vantaggi e svantaggi:

    • Utilizzo di diversi domini di primo livello (TLD): www.tuosito.com, www.tuosito.it, ecc...
      Soluzione da adottare solo per siti localizzati (non tradotti). Ha il vantaggio che il sito viene identificato come più "vicino" alla country, sia da parte dell'utente (psicologicamante preferisce vedere .it che .com o .org...), sia da parte dei motori di ricerca (il sito viene preferito nelle SERPs).
      Essendo ritenuti dai motori di ricerca come siti completamente diversi, i domini non soffrono di eventuali penalizzazioni in termini di ranking dovute ad errori nelle altre versioni localizzate.
      Per contro, tale soluzione richiede di dover registrare (e quindi pagare) più domini, dovendo affrontare anche le differenti gestioni burocratiche della registrazione del dominio nei vari stati.
      Attenzione anche all'uso di cookie...sono siti diversi, dunque non possonio essere condivisi!

    • Utilizzo di diversi sottodomini: en.tuosito.com, it.tuosito.com, ecc...
      Soluzione adatta sia nel caso di siti localizzati che tradotti. Essendo il dominio di primo livello uguale per tutte le versioni del sito, parte della "reputazione" viene ereditata dalle altre versioni localizzate.

    • Utilizzo di diverse cartelle del sito: www.tuosito.com/en, www.tuosito.com/it, ecc...
      Essendo il dominio identico per tutte le versione, la "reputazione" viene completamente ereditata. Una penalizzazione del ranking di una versione condizionerebbe la popolarità di tutte le verioni localizzate.
      Per tale motivo mi sento di sconsigliarla nel caso di siti localizzati, mentre può essere utilizzata con buona tranquillità nel caso di siti tradotti.
      Tale soluzione inoltre è la più semplice da implementare e non richiede alcuna configurazione a livello di infrastruttura.