¿La mejor manera de implementar el Patrón de Repositorio?


He estado explorando BDD/DDD y, como consecuencia, tratando de llegar a una implementación adecuada del patrón de repositorio. Hasta ahora, ha sido difícil encontrar un consenso sobre la mejor manera de implementar esto. He intentado reducirlo a las siguientes variaciones, pero no estoy seguro de cuál es el mejor enfoque.

Como referencia estoy construyendo un ASP.Aplicación MVC con NHibernate como 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);
}

Mis pensamientos iniciales son que

1) es grande desde un punto de eficiencia de vista, pero puedo meterme en problemas a medida que las cosas se complican.

2) parece muy tedioso y podría terminar con una clase muy concurrida, pero por lo demás ofrece un alto grado de separación entre mi lógica de dominio y la capa de datos que me gusta.

3) parece difícil por adelantado y más trabajo para escribir consultas,pero limita la contaminación cruzada solo a la capa de especificaciones.

4) Mi menos favorito, pero posiblemente la implementación más directa y posiblemente la base de datos más eficiente para complejos consultas, aunque pone mucha responsabilidad en el código de llamada.

Author: Jim G., 2009-09-11

7 answers

Creo que todas son buenas opciones (excepto quizás 4 si no quieres atarte a nhibernate), y parece que tienes pros y contras bien analizados para tomar una decisión por tu cuenta basada en tu esfuerzo actual. No te golpees a ti mismo demasiado con esto.

Actualmente estoy trabajando en una mezcla entre 2 y 3 Supongo:

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);
}

Y también hay un Repositorio (de T) clase base.

 10
Author: Fredy Treboux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-11 02:43:39

También hay un buen argumento para un enfoque "ninguno de los anteriores".

El problema con los repositorios genéricos es que está asumiendo que todos los objetos en su sistema soportarán las cuatro operaciones CRUD: Crear, Leer, Actualizar, Eliminar. Pero en sistemas complejos, es probable que tenga objetos que solo admiten algunas de las operaciones. Por ejemplo, puede tener objetos que son de solo lectura u objetos que se crean pero nunca se actualizan.

Usted podría romper el Interfaz IRepository en pequeñas interfaces, para Leer, Borrar, etc. pero eso se complica bastante rápido.

Gregory Young hace un buen argumento (desde una perspectiva de estratificación DDD / software) de que cada repositorio debe soportar solo las operaciones que son específicas para el objeto de dominio o agregado con el que está trabajando. Aquí está su artículo sobre repositorios genéricos.

Y para una vista alternativa, vea esta entrada de blog de Ayende .

 22
Author: dthrasher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-12 00:14:39

Una de las cosas que estamos haciendo es que todos nuestros repositorios tienen diferentes necesidades por lo que estamos creando una colección de interfaces:

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

En este ejemplo, el repositorio de solo lectura simplemente obtiene de la base de datos. La razón de la T,V es que la V representa lo que es devuelto por el repositorio y la T representa lo que se pasa, por lo que podría hacer algo como esto:

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

    public Customer Find(string customerName)
    {
    }
}

También puedo crear interfaces separadas para Agregar, Actualizar y Eliminar. De esta manera si mi el repositorio no necesita el comportamiento, entonces simplemente no implementa la interfaz.

 6
Author: Michael Mann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-11 03:58:33

Soy fan de bif de 1 porque puedo crear filtros y métodos de extensión de paginación que puedo aplicar a los valores de retorno de IQueryable para el método Find. Mantengo los métodos de extensión en la capa de datos y luego construyo sobre la marcha en la capa de negocios. (No totalmente puro, es cierto.)

Por supuesto, cuando el sistema se estabiliza tengo la opción de hacer métodos de Búsqueda específicos utilizando los mismos métodos de extensión y optimizar utilizando Func.

 1
Author: Scott McKenzie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-11 03:11:46

Cuando se usa NH con Linq, el repositorio puede ser:

session.Linq<Entity>()

Las especificaciones son cosas que tratan con:

IQueryable<Entity>

Puedes fingir todo si quieres, pero eso es mucho trabajo mundano para abstraer una abstracción.

Simple es bueno. Sí, NH hace bases de datos, pero proporciona muchos más patrones. Tener otro que el DAL depende de NH está lejos de ser un pecado.

 1
Author: anonymous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-11 14:55:10

Creo que depende de sus necesidades. Es importante pensar en el repositorio junto con otros patrones de diseño que considere usar. Finalmente, depende mucho de lo que esperas del repositorio (cuáles son las principales razones para usarlo).

¿Necesita crear una capa estricta (por ejemplo, necesitará reemplazar NHibernate con Entity Framework en el futuro)? ¿Quieres escribir test especialmente en los métodos del repositorio?

No hay una mejor manera de crear repositorio. Solo hay algunas maneras y definitivamente depende de ti, cuál es la más práctica para tus necesidades.

 1
Author: Miroslav Holec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-07 18:17:43

Se marchita el Repositorio

Un artículo relevante de Jimmy Bogard de LosTechies

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

Además, otro artículo rápido con algunos comentarios que sugieren que la versión #2 es realmente un patrón DOA y no un repositorio.

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

 0
Author: Michael Cook,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-11 16:21:28