Crear una capa de abstracción sobre la capa ORM

12

Creo que si tiene sus repositorios use un ORM que ya está lo suficientemente abstraído de la base de datos.

Sin embargo, donde estoy trabajando ahora, alguien cree que deberíamos tener una capa que abstraiga el ORM en caso de que nos gustaría cambiar el ORM más adelante.

¿Es realmente necesario o es simplemente una gran cantidad de gastos generales para crear una capa que funcione en muchos ORM?

Editar

Solo para dar más detalles:

  1. Tenemos clase POCO y clase de entidad que están mapeadas con AutoMapper. La capa de repositorio utiliza la clase de entidad. La capa de repositorio luego usa la capa adicional de abstracción para comunicarse con Entity Framework.
  2. La capa empresarial no tiene acceso directo a Entity Framework. Incluso sin la capa adicional de abstracción sobre el ORM, esta necesita usar la capa de servicio que usa la capa de repositorio. En ambos casos, la capa empresarial está totalmente separada del ORM.
  3. El argumento principal es poder cambiar ORM en el futuro. Dado que está realmente localizado dentro de la capa del Repositorio, para mí, ya está bien separado y no veo por qué se requiere una capa adicional de abstracción para tener un código de "calidad".
Patrick Desjardins
fuente
3
la capa adicional necesita justificación, de lo contrario, viola YAGNI. En otras palabras, alguien que cree que lo necesita, tiene la carga de demostrarlo
mosquito el
2
Puedo entender querer una capa de dominio que solo exponga un subconjunto de operaciones deseado: los ORM tienden a ser un área de superficie demasiado amplia (digamos que no desea permitir actualizaciones a una entidad no dirigida por otra entidad que lo contenga). Tener tal capa de abstracción ayuda con esto.
Oded
44
Probablemente necesitará una segunda capa de abstracción para la primera capa de abstracción por encima del ORM en caso de que desee cambiar también la primera capa.
David Peterman
1
@David Mientras agregamos reducción, cambie todos sus if (boolean) a if (boolean == true) y si desea regurgitar más de lo mismo, if (boolean == true == true ...) y así sucesivamente
Brian
1
Publicación
RMalke

Respuestas:

12

De esa manera se encuentra la locura. Es muy poco probable que alguna vez necesite cambiar ORM. Y si alguna vez decide cambiar el ORM, el costo de reescribir las asignaciones será una pequeña fracción del costo para desarrollar y mantener su propio meta-ORM. Esperaría que pudiera escribir algunos scripts para hacer el 95% del trabajo necesario para cambiar ORM.

Los marcos internos son casi siempre un desastre. Construir uno anticipándose a las necesidades futuras es casi un desastre garantizado. Los marcos exitosos se extraen de proyectos exitosos, no se construyen con anticipación para satisfacer necesidades imaginarias.

Kevin Cline
fuente
12

El ORM proporciona una abstracción para que su capa de datos sea independiente de su RDBMS, pero puede no ser suficiente para "desatar" su capa empresarial de su capa de datos. Específicamente, no debe permitir que los objetos que se asignan a tablas RDBMS se "filtren" directamente en la capa empresarial.

Como mínimo, su capa empresarial necesita programar en interfaces que sus objetos mapeados en tabla y administrados por ORM de la capa de datos podrían implementar. Además, es posible que deba crear una capa basada en la interfaz de construcción de consultas abstractas para ocultar las capacidades de consulta nativas de su ORM. El objetivo principal es evitar "introducir" cualquier ORM particular en su solución más allá de su capa de datos. Por ejemplo, puede ser tentador crear cadenas HQL ( Hibernate Query Language ) en la capa empresarial. Sin embargo, esta decisión aparentemente inocente vincularía su capa empresarial con Hibernate, lo que uniría a la empresa y las capas de acceso a datos; Debe intentar evitar esta situación tanto como sea posible.

EDITAR: en su caso, la capa adicional dentro del repositorio es una pérdida de tiempo: según su punto número dos, su capa empresarial está suficientemente aislada de su repositorio. Proporcionar aislamiento adicional introduciría una complejidad innecesaria, sin beneficios adicionales.

El problema con la construcción de una capa de abstracción adicional dentro de su repositorio es que la "marca" particular de ORM dicta la forma en que interactúa con él. Si crea un envoltorio delgado que se parece a su ORM, pero está bajo su control, reemplazar el ORM subyacente será aproximadamente tan difícil como lo sería sin esa capa adicional. Si, por otro lado, construye una capa que no se parece en nada a su ORM, entonces debe cuestionar su elección de la tecnología de mapeo relacional de objetos.

dasblinkenlight
fuente
Estoy muy contento de que .NET haya resuelto este problema integrando el objeto de consulta en la plataforma. El puerto .NET de Hibernate incluso lo admite.
Michael Brown
2
@MikeBrown Yeah, y .NET también suministraron dos tecnologías ORM de la competencia, ¡ambas usando la tecnología LINQ!
dasblinkenlight
@dasblinkenlight Actualicé la pregunta para darle información adicional.
Patrick Desjardins
Últimamente, he adoptado el enfoque de hacer que la capa empresarial dependa de una capa de datos basada en interfaces similares a mapas y conjuntos para almacenar el estado. Ahora creo que esas interfaces representan la preocupación de mantener el estado lo suficientemente bien (inspirado en el estilo funcional de programación) y proporcionan una buena separación del ORM de elección a través de una implementación bastante delgada.
beluchin
2

UnitOfWork generalmente proporciona esta abstracción. Es un lugar que necesita cambiar, sus repositorios dependen de él a través de una interfaz. Si alguna vez necesita cambiar O / RM, simplemente implemente un nuevo UoW sobre él. Uno y listo.

Por cierto, va más allá de simplemente cambiar O / RM, piense en las pruebas unitarias. Tengo tres implementaciones de UnitOfWork, una para EF, una para NH (porque realmente tuve que cambiar las O / RM a mitad del proyecto para un cliente que quería soporte de Oracle), y una para la persistencia de InMemory. La persistencia de InMemory era perfecta para pruebas unitarias o incluso para la creación rápida de prototipos antes de que estuviera listo para poner una base de datos detrás de ella.

El marco es simple de implementar. Primero tienes tu interfaz genérica IRepository

public interface IRepository<T>
  where T:class
{
  IQueryable<T> FindBy(Expression<Func<T,Bool>>filter);
  IQueryable<T> GetAll();
  T FindSingle(Expression<Func<T,Bool>> filter);
  void Add(T item);
  void Remove(T item);

}

Y la interfaz IUnitOfWork

public interface IUnitOfWork
{
   IQueryable<T> GetSet<T>();
   void Save();
   void Add<T>(T item) where T:class;
   void Remove<T>(T item) where T:class;
}

El siguiente es el repositorio base (su elección sobre si debe ser abstracto o no)

public abstract class RepositoryBase<T>:IRepository<T>
  where T:class
{
   protected readonly IUnitOfWork _uow;

   protected RepositoryBase(IUnitOfWork uow)
   { 
      _uow=uow;
   }

   public IQueryable<T> FindBy(Expression<Func<T,Bool>>filter)
   {
      return _uow.GetSet<T>().Where(filter);
   }

   public IQueryable<T> GetAll()
   {
      return _uow.GetSet<T>();
   }

   public T FindSingle(Expression<Func<T,Bool>> filter)
   {
      return _uow.GetSet<T>().SingleOrDefault(filter);
   }

   public void Add(T item)
   {
      return _uow.Add(item);
   }

   public void Remove(T item)
   {
      return _uow.Remove(item);
   }
}

Implementar IUnitOfWork es un juego de niños para EF, NH e In Memory. La razón por la que devuelvo IQueryable, es por la misma razón, mencionó Ayende en su publicación, el cliente puede filtrar, ordenar, agrupar e incluso proyectar aún más el resultado usando LINQ y aún obtendrá el beneficio de que todo se haga desde el lado del servidor.

Michael Brown
fuente
1
Pero la pregunta aquí es determinar si esa capa anterior es útil o no y si debería ser el guardián de todos los accesos a los datos.
Brian
Ojalá pudiera señalar mi publicación de blog sobre la implementación de la Unidad de trabajo / repositorio. Discute las preocupaciones exactas del OP.
Michael Brown
Darle un nombre a la capa no significa que sea necesario o útil.
Kevin Cline
Tenga en cuenta que según el OP, tiene una asignación adicional entre el acceso a datos y la capa empresarial. Para mí, mis objetos de negocio y objetos de entidad son los mismos. EF y NH proporcionan sorprendentes API de mapeo de tal manera que el mapeo de datos rara vez (si alguna vez) se convierte en una preocupación.
Michael Brown
¿Cómo se traduce una expresión arbitraria en una llamada ORM eficiente? No puede buscar todo y tirar las filas que no coinciden con el filtro.
Kevin Cline