mercoledì 8 giugno 2011

Enterprise Design: Repository Pattern

In accordo con Martin Fowler il repository pattern è: "layer of abstraction over the mapping layer where query construction code is concentrated". In pratica è una collezione di metodi per l'accesso ai dati e visibili alle classi di dominio. Ancora meglio: il repository pattern crea un livello di astrazione sopra l'ORM. Abbiamo subito tre vantaggi concreti:
  1. Testabilità: utilizzando il repository pattern otteniamo un layer su cui è semplice sostituire i dati con delle classi stub per effettuare test sulla logica di business senza interessare il codice di accesso ai dati.
  2. Astrazione: questa atrazione permette di modificare di disaccoppiare la logica di business dallo strato di accesso ai dati.
  3. Dependency Injection: permette di utilizzare containers Di per iniettare oggetti che si vogliono utilizzare nel codice.
Si parte dalla creazione di un'interfaccia, nel mio caso:
public interface IShopRepository
{
IList<
Shop> FindAll();
IList
<Shop> FindAll(int index, int count);
IList
<Shop> GetShopByCategory(int idcategory);
IList
<Shop> GetPrimoPiano();
IList
<Shop> GetAltri();
Shop FindById(int id);
Shop FindByName(string name);
IList
<Category> GetAllCategory();
IList<Product> GetProductBoxMostSell();
IList<ShopAffiliato> GetAllShopAffiliati();
(...)

}
In questo caso abbiamo solo operazioni di "select", in generale avremo tutte le operazioni cosidette CRUD (create, read, update, delete).

Il secondo passo è costruire una classe che implementa i metodi definiti nella interfaccia. Nel mio caso questa classe interagisce con l'ORM sottostante: Entity Framework.

 IList<Shop> IShopRepository.FindAll()
{

var negozi = from n in new IperclubShopEntities().tShops
where n.attivo == true
select new Shop
{
Id = n.ShopId,
Nome = n.nome,
Attivo = (bool)n.attivo,
CodiceZanox = n.codicezanox,
Descrizione = n.descrizione,
Logo1 = n.logo1,
Logo2 = n.logo2,
PrimoPiano = (bool)n.primopiano,
ScopriAltri = (bool)n.scoprialtri,
TipoConversione = 1,
TassoConversione = n.tassoconversione,
IndiceConversione = (float)n.conversione,
UrlNegozio = n.url,
TestoVetrina=n.testovetrina,
ListaBrand = from b in n.tBrands
where b.attivo == true
select new Brand
{
Attivo = (bool)b.attivo,
BrandId = b.BrandId,
Descrizione = b.descrizione,
Logo1 = b.logo1,
Logo2 = b.logo2,
Nome = b.nome
},
ListaTag = from t in n.tTags
select new Tag
{
Id = t.TagId,
Nome = t.tag
},
ListaCategory = from c in n.tCategories
select new Category
{
Id = c.CategoryId,
Nome = c.nome
},
ListaRegole = from r in n.tRules
select new Rules
{
RegolaId = r.RegolaId,
Attiva = (bool)r.attiva,
Conversione = (float)r.conversione,
Note = r.note,
Punti = (int)r.punti,
Shopid = (int)r.Shop_id,
TestoRegola = r.testoregola,
TipoRegola = (int)r.tiporegola,
ValoreMassimo = (decimal)r.valoremassimo,
ValoreMinimo = (decimal)r.valoreminimo
},
Tiponegozio = new TipiNegozio{ Id=n.tTipiNegozio.id, Nome=n.tTipiNegozio.tipo},
};

return negozi.ToList();
}

Ora non rimane che dire implementare nel layer dei servizi una classe che istanzi un oggetto di tipo IShopRepository e ne utilizzi i metodi per ottenere collezioni che possibilmente abbiano implementata l'interfaccia IEnumerable
public class ShopService
{
private IShopRepository _shoprepository;
public
ShopService(IShopRepository shoprepository)
{
_shoprepository = shoprepository;
}

public
IEnumerable<ShopViewModel> GetAll()
{
return
_shoprepository.FindAll().ConvertToShopViewList();
}
(....)
}

In questo spezzone di codice viene fatto anche il mapping tra gli oggetti del model e gli oggetti del viewmodel.

Dove avviene la magia?

Nel progetto del sito MVC ho creato una classe BootStrapper che si occupa di assegnare ad una richiesta di un tipo astratto un tipo concreto:

public class BootStrapper
{ public static void ConfigureDependencies()
{ ObjectFactory.Initialize(x=>
{
x.AddRegistry<ControllerRegistry>();
}); }

public class ControllerRegistry : Registry
{
public ControllerRegistry()
{
ForRequestedType<ICategoryRepository>().TheDefault.Is.OfConcreteType<CategoryRepository>();
ForRequestedType<IShopRepository>().TheDefault.Is.OfConcreteType<ShopRepository>();
ForRequestedType<IPartnerRepository>().TheDefault.Is.OfConcreteType<PartnerRepository>(); }
}
}


Nell'evento Application_start:

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
BootStrapper.ConfigureDependencies();
ControllerBuilder.Current.SetControllerFactory(new IoCControllerFactory());
}


E il gioco è fatto. Il layer dei servizi è completamente disaccoppiato dal modo con cui si accede ai dati.