Domanda

Ho esplorato BDD / DDD e di conseguenza ho cercato di realizzare una corretta implementazione del modello di repository. Finora è stato difficile trovare un consenso sul modo migliore per implementarlo. Ho provato a ridurlo alle seguenti varianti, ma non sono sicuro di quale sia l'approccio migliore.

Per riferimento sto creando un'applicazione ASP.MVC con NHibernate come back-end.

public interface IRepository<T> {
        // 1) Thin facade over LINQ
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IQueryable<T> Find();
        // or possibly even
        T Get(Expression<Func<T, bool>> query);
        List<T> Find(Expression<Func<T, bool>> query);
}

public interface IRepository<T> {
        // 2) Custom methods for each query
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> FindBySku(string sku);
        IList<T> FindByName(string name);
        IList<T> FindByPrice(decimal price);
        // ... and so on
}

public interface IRepository<T> {
        // 3) Wrap NHibernate Criteria in Spec pattern
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> FindBySpec(ISpecification<T> specification);
        T GetById(int id);
}


public interface IRepository<T> {
        // 4) Expose NHibernate Criteria directly
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> Find(ICriteria criteria);
        // .. or possibly
        IList<T> Find(HQL stuff);
}

I miei pensieri iniziali sono quelli

1) è ottimo dal punto di vista dell'efficienza, ma potrei avere problemi quando le cose si complicano.

2) sembra molto noioso e potrebbe finire con una classe molto affollata, ma per il resto offre un alto grado di separazione tra la logica del mio dominio e il livello dati che mi piace.

3) sembra difficile in anticipo e più lavoro per scrivere query, ma limita la contaminazione incrociata solo al livello Specifiche.

4) La mia implementazione meno preferita, ma probabilmente la più diretta e probabilmente la maggior parte del database efficiente per query complesse, sebbene metta molta responsabilità sul codice chiamante.

È stato utile?

Soluzione

Penso che siano tutte buone opzioni (tranne forse 4 se non vuoi legarti per proibire), e sembra che tu abbia pro e contro ben analizzati per prendere una decisione da solo sulla base del tuo attuale impegno. Non batterti troppo su questo.

Attualmente sto lavorando a un mix tra 2 e 3, suppongo:

public interface IRepository<T> 
{
        ...
        IList<T> FindAll();
        IList<T> FindBySpec(ISpecification<T> specification);
        T GetById(int id);
}

public interface ISpecificRepository : IRepository<Specific> 
{
        ...
        IList<Specific> FindBySku(string sku);
        IList<Specific> FindByName(string name);
        IList<Specific> FindByPrice(decimal price);
}

E c'è anche una classe base del repository (di T).

Altri suggerimenti

C'è anche un buon argomento per un " nessuno dei precedenti " approccio.

Il problema con i repository generici è che stai assumendo che tutti gli oggetti nel tuo sistema supporteranno tutte e quattro le operazioni CRUD: Crea, Leggi, Aggiorna, Elimina. Ma in sistemi complessi, probabilmente avrai oggetti che supportano solo alcune delle operazioni. Ad esempio, potresti avere oggetti di sola lettura o oggetti creati ma mai aggiornati.

Potresti spezzare l'interfaccia IRepository in piccole interfacce, per Leggi, Elimina, ecc. ma questo diventa piuttosto disordinato.

Gregory Young fa una buona argomentazione (dal punto di vista della stratificazione DDD / software) secondo cui ogni repository dovrebbe supportare solo le operazioni specifiche dell'oggetto di dominio o aggregato con cui stai lavorando. Ecco il suo articolo su repository generici .

E per una vista alternativa, vedere questo Ayende post di blog .

Una delle cose che stiamo facendo è che tutti i nostri repository hanno esigenze diverse, quindi stiamo creando una raccolta di interfacce:

public interface IReadOnlyRepository<T,V>
{
   V Find(T);
}

In questo esempio il repository di sola lettura viene dal database. Il motivo di T, V è che V rappresenta ciò che viene restituito dal repository e T rappresenta ciò che viene passato, quindi è possibile fare qualcosa del genere:

public class CustomerRepository:IReadOnlyRepository<int, Customer>, IReadOnlyRepository<string, Customer>
{
    public Customer Find(int customerId)
    {
    }

    public Customer Find(string customerName)
    {
    }
}

Posso anche creare interfacce separate per Aggiungi, Aggiorna ed Elimina. In questo modo se il mio repository non ha bisogno del comportamento, semplicemente non implementa l'interfaccia.

Sono un fan di bif di 1 perché posso creare filtri e metodi di estensione di paging che posso applicare a IQueryable < > restituisce valori per il metodo Trova. Conservo i metodi di estensione nel livello dati e quindi li costruisco al volo nel livello aziendale. (Non del tutto puro, è vero.)

Naturalmente, quando il sistema si stabilizza, ho la possibilità di creare metodi Find specifici utilizzando gli stessi metodi di estensione e ottimizzare usando Func < > ;.

Quando si utilizza NH con Linq il repository può essere:

session.Linq<Entity>()

Le specifiche sono cose che riguardano:

IQueryable<Entity>

Se lo desideri, puoi fare la facciata di distanza, ma è un lavoro molto banale per astrarre un'astrazione.

Semplice è buono. Sì, NH crea database, ma fornisce molti più schemi. Avere altro che il DAL dipende da NH è tutt'altro che un peccato.

Credo che dipenda dalle tue esigenze. È importante pensare al repository insieme ad altri schemi di progettazione che si ritiene di utilizzare. Infine, dipende molto da cosa ti aspetti dal repository (quali sono i motivi principali per usarlo).

Devi creare un livello rigoroso (ad esempio dovrai sostituire NHibernate con Entity Framework in futuro)? Vuoi scrivere test soprattutto sui metodi del repository?

Non esiste un modo migliore per creare un repository. Ci sono solo alcuni modi ed è sicuramente da te, qual è il più pratico per le tue esigenze.

Appassisci il repository

Un articolo pertinente di Jimmy Bogard di LosTechies

http: // www .lostechies.com / blogs / jimmy_bogard / archive / 2009/09/10 / appassire-the-repository.aspx

Inoltre, un altro breve articolo con alcuni commenti che suggeriscono che la versione 2 è in realtà un modello DOA e non un repository.

http://fabiomaulo.blogspot.com/2009/06 /linq-and-repository.html

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top