Pregunta

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 creando una aplicación ASP.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 excelente desde el punto de vista de la eficiencia, 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 requiere más trabajo para escribir consultas, pero limita la contaminación cruzada solo a la capa de especificaciones.

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

¿Fue útil?

Solución

Creo que todas son buenas opciones (excepto quizás 4 si no quieres atarte a nhibernate), y parece que tienes los pros y los contras bien analizados para tomar una decisión por tu cuenta en función de 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 una clase base de Repositorio (de T).

Otros consejos

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

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

Puede dividir la interfaz IRepository en interfaces pequeñas, para Leer, Eliminar, etc., pero eso se vuelve bastante rápido.

Gregory Young hace un buen argumento (desde una perspectiva de capas DDD / software) de que cada repositorio debe admitir solo las operaciones que son específicas del 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 este Ayende publicación de blog .

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 solo se obtiene de la base de datos. La razón para T, V es que V representa lo que devuelve el repositorio y 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 repositorio no necesita el comportamiento, simplemente no implementa la interfaz.

Soy un fanático de bif de 1 porque puedo crear filtros y métodos de extensión de paginación de los que puedo aplicar a IQueryable < > valores de retorno 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 empresarial. (No es totalmente puro, lo admito).

Por supuesto, cuando el sistema se estabiliza, tengo la opción de hacer métodos de búsqueda específicos con los mismos métodos de extensión y optimizarlos con Func < > ;.

Cuando use NH con Linq, su repositorio puede ser:

session.Linq<Entity>()

Las especificaciones son cosas que tratan:

IQueryable<Entity>

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

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

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

¿Necesita crear una capa estricta (por ejemplo, deberá reemplazar NHibernate con Entity Framework en el futuro)? ¿Desea escribir prueba especialmente para los métodos de repositorio?

No hay mejor manera de crear un repositorio. Hay solo algunas maneras y definitivamente depende de usted, cuál es la más práctica para sus necesidades.

Wither the Repository

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top