Quando si progetta una applicazione basata su un Domain Model (nel mio caso un Entity Data Model di EF) il cui service layer dovrà essere esposto con servizi WCF ci si scontra con un dubbio: serializzo le Entity o mi creo dei DTO per spostare i dati da un layer (e spesso anche tier) all'altro?
Questo dubbio ha una risposta scontata, che molti architetti ti darebbero senza chiedere nemmeno delucidazioni sul tipo di progetto: DTO forever. Ebbene anch'io sono arrivato a questa conclusione e ci sono voluto arrivare per gradi. Si perchè, da buon testardo che sono, ho voluto provare sulla mia pelle una soluzione che non prevedesse alcun DTO.
In questo post voglio brevemente condividere la mia esperienza e raccogliere quel paio di osservazioni che mi hanno fatto decidere, a metà progetto, di eseguire un Refactoring to DTO - se si può dire ;) -
Innanzittutto la prima banale osservazione: quando serializzi una Entity stai trasferendo una mole di dati "importante", spesso tra tier fisici; spesso di questa mole di dati te ne serve un 30%. Dopo l'adozione di DTO si nota, anche ad occhio, un sensibile miglioramente nei tempi di risposta del sistema.
La seconda è che ti ritrovi sul consumatore del servizio una serie di classi che espongono metodi potenzialmente pericolosi. Un esempio su tutti sono i metodi Load delle EntityCollection ed EntityReference, che ci ritroviamo sul client, ma che dal client non possono essere invocati essendo l'ObjectContext non più presente.
La terza osservazione riguarda il Lazy Load: o il serializzatore serializza sempre l'intero grafo (e già sarebbe un problema!) oppure implementare LazyLoad sulle Entity (come per esempio offre NHibernate gratis) potrebbe diventare a sua volta pericoloso, perchè potresti potenzialmente avere sul client una istanza con una proprietà che si carica Lazy non ancora caricata; ci si ritroverebbe ancora ad invocare un metodo che richiede probabilemente un ObjectContext che non abbiamo sul client.
La quarta osservazione (quella che mi ha obbligato a passare ai DTO, perchè causava il non funzionamento dell'applicazione) riguarda il ChangeTracking di EF. Entity Framework mantiene una serie di informazioni sullo stato delle Entity appartenenti ad un ObjectContext e queste informazioni le distribuisce un po' all'interno dell'ObjectContext stesso e un po' all'interno delle Entity. In un contesto distribuito è verosimile che succeda che l'applicazione richieda in tempi diversi Entity diverse a ObjectContext diversi e che infine queste Entity debbano essere associate tra di loro e riassegnate ad un ulteriore ObjectContext: tutto questo con oggetti POCO non sarebbe un problema. Invece usando direttamente le Entity si deve tenere conto delle informazioni di stato di una Entity che possono impedire a volte la serializzazione a volte la migrazione di ObjectContext. Ne ho provate tante; anche la soluzione più pulita, ovvero leggere le entity da serializzare con MergeOption impostato a NoTracking, che sembrava funzionare in tutti i casi, in realtà sfociava in errori inaspettati quando si creavano nuove associazioni sulle entity.
Sul quarto punto potrei raccogliere tutte le prove e le pseudo-soluzioni trovate in un altro post.
Per ora mi fermo qui e sottolineo anch'io, dopo queste esperienza, che se una applicazione è distribuita, non si può fare a meno di usare dei DTO per migrare i dati tra i vari Layer (Tier).
Matteo