Nella scorso post di questa serie ho esposto un possibile refactoring della classe di esempio, al fine di renderla compatibile con l'applicazione degli unit test.
Qui continuerò la stessa attività, al fine di risolvere la dipendenza ancora rimasta. Infatti è facile verificare che permane nella classe la logica che si occupa dell'accesso alla base dati e questo, come detto, no buono !
Più esplicitamente il problema è che nella classe viene usato esplicitamente la classe LocalDbContext, che è il context di entity framework per interagire con il database
Introduzione del repository pattern
Per risolvere questa dipendenza useremo un approccio molto simile a quello visto per sistemare l'invio email: solo che in questo caso la cosa diventa leggermente più complessa. Inoltre il problema in analisi (cioè le dipendenze derivanti dalla necessità di accedere a una base dati) è talmente generalizzato che esiste, per risolverlo, un pattern ben definito, chiamato repository pattern.
Questo è stato creato creare un ulteriore livello di astrazione all'accesso ai dati. In pratica implementando il pattern si separa la logica di accesso alla base dati alle logiche di business: in soldoni la classe InvioMailNewsLetter (logica di business) nulla deve conoscere su come ricavare i dati, e quindi non deve in alcun modo avere alcun riferimento diretto a entity framework.
Ecco quindi il codice modificato in modo tale da utilizzare il pattern repository.
Mi permetto di farVi notare il costruttore della classe è marcato con l'access modifier protected: l'intento è di nascondere il costruttore di default per obbligare ad usare solo quello con parametri. Infatti è evidente che per usare la classe in modo sensato è indispensabile passargli le istanze degli oggetti per l'invio mail e interazione con il databasenel costruttore, ed evitare che dentro la classe stessa si instanzino oggetti di questo tipo. Certo, a rigor di logica il modificatore dovrebbe essere private, ma vedremo tra poco invece perchè è stata marcato come protected. In ogni caso l'intento è stato raggiunto.
Quindi la classe InvioMailNewsLetter ora assume l'aspetto seguente.
La classe di repository è l'unica delegata a usare il prescelto sistema per l'accesso alla base dati: qui abbiamo complicato leggermente la cosa facendo il metodo generic. in modo tale da poterlo riutilizzare anche con altre entità e in altri contesti.
Anche con questo refactoring abbiamo ottenuto analoghi vantaggi del refactoring delle mail,
Mi permetto di aggiungere che qui, centralizzando l’accesso alla base dati, è possibile facilmente implementare logiche di caching dei dati.
Ehi: ma qualcosa non mi torna con il repository pattern !
Quanto visto sopra è perfettamente funzionante, e in realtà abbraccia il 99 % degli utilizzi pratici: potrei fermarmi senza aver nessun senso di colpa.
Però quando poi si inizia a lavorare con il repository pattern praticamente ci si accorge da subito di un grosso limite: se si devono fare modifiche che coinvolgono più tabelle/entità si rischia di fare un disastro.
Se c’è qualche problema per esempio di connettività o altro si corre il rischio di eseguire i salvataggi su Entita01 e non in Entita02 (per esempio se si interrompe la connettività nel punto critico): questo può portare a un problema sulla consistenza semantica dei dati.
Quindi il problema di cui soffre il repository pattern così come esposto sopra è che quando lo si usa per eseguire salvataggi che coinvolgono più di una entità esiste una seppur remota possibilità di introdurre delle inconsistenze dei dati trattati.
Per ovviare a questo è possibile completare il pattern con un ulteriore pattern, unit of work, che in pratica è un'estensione che si occupa proprio di salvataggi cross-entity.
Osservo comunque che per tamponare il problema esposto esistono anche altri metodi, oltre al citato pattern: però in un modo o nell'altro scimmiottano o copiano malamente unit of work o introducono strutture barocche e poco lineari.
Vi accenno solo la problematica, anche perchè l'esposizione di questo pattern ci porterebbe troppo lontano. Daltronde in linkografia trovate un paio di riferimenti assolutamente esaustivi sull’argomento.
Linkografia
Informatica pressapochista - Un viaggio negli unit-test: Introduzione - Prima parte
Informatica pressapochista - Un viaggio negli unit-test: xUnit e dintorni – Seconda parte
Informatica pressapochista - Un viaggio negli unit-test: Fake class e dintorni - Terza parte
Git Hub Page Informatica Pressapochista
Programming with Wolfgang: Repository and Unit of Work Pattern
C# corner - Repository And UnitOfWork Pattern