Создание слоя абстракции над слоем ORM

12

Я считаю, что если у вас есть ваши репозитории, используйте ORM, который уже достаточно абстрагирован от базы данных.

Однако там, где я сейчас работаю, кто-то считает, что у нас должен быть слой, который абстрагирует ORM на случай, если мы захотим изменить ORM позже.

Это действительно необходимо, или это просто слишком много, чтобы создать слой, который будет работать на многих ORM?

редактировать

Просто чтобы дать более подробную информацию:

  1. У нас есть класс POCO и класс сущностей, которые отображаются с помощью AutoMapper. Класс сущности используется слоем Repository. Затем уровень хранилища использует дополнительный уровень абстракции для взаимодействия с Entity Framework.
  2. Бизнес-уровень никоим образом не имеет прямого доступа к Entity Framework. Даже без дополнительного уровня абстракции над ORM необходимо использовать уровень обслуживания, который использует уровень хранилища. В обоих случаях бизнес-уровень полностью отделен от ORM.
  3. Основным аргументом является возможность изменить ORM в будущем. Поскольку он действительно локализован внутри слоя Repository, для меня он уже хорошо отделен, и я не понимаю, почему для создания «качественного» кода требуется дополнительный уровень абстракции.
Патрик Дежарден
источник
3
дополнительный слой нуждается в обосновании, иначе он нарушает ЯГНИ. Другими словами, тот , кто верит, что вам это нужно, должен доказать это
комнат
2
Я могу понять, что требуется доменный уровень, который предоставляет только желаемое подмножество операций - ORM имеют тенденцию быть слишком широкими по площади (скажем, вы не хотите разрешать обновления сущности, не направленной другой содержащей сущность). Наличие такого слоя абстракции помогает в этом.
Одед
4
Вероятно, вам понадобится второй уровень абстракции для первого уровня абстракции над ORM на тот случай, если вы также захотите изменить первый уровень.
Дэвид Петерман
1
@David В то время как мы добавляем избыточность, измените все ваши if (логическое) на if (логическое == true), и если вы хотите отрыгнуть больше того же самого, if (boolean == true == true ...) и так далее
Брайан
1
Интересный связанный пост: ayende.com/blog/3955/repository-is-the-new-singleton
RMalke

Ответы:

12

Так лежит безумие. Крайне маловероятно, что вам когда-либо потребуется изменить ORM. И если вы когда-нибудь решите изменить ORM, затраты на переписывание сопоставлений составят ничтожную долю затрат на разработку и поддержку вашей собственной мета-ORM. Я ожидаю, что вы могли бы написать несколько сценариев для выполнения 95% работы, необходимой для переключения ORM.

Внутренние структуры - почти всегда катастрофа. Строительство одного в ожидании будущих потребностей - почти гарантированная катастрофа. Успешные структуры извлекаются из успешных проектов, а не создаются заранее для удовлетворения воображаемых потребностей.

Кевин Клайн
источник
12

ORM предоставляет абстракцию для вашего уровня данных, чтобы он не зависел от своей РСУБД, но этого может быть недостаточно, чтобы «отвязать» ваш бизнес-уровень от уровня данных. В частности, вы не должны позволять объектам, которые сопоставляются с таблицами RDBMS, «просачиваться» непосредственно в бизнес-уровень.

По крайней мере, вашему бизнес-уровню необходимо программировать для интерфейсов, которые потенциально могут реализовать управляемые ORM объекты с отображением таблиц из уровня данных. Кроме того, вам может потребоваться создать интерфейсный слой построения абстрактных запросов, чтобы скрыть собственные возможности запросов в ORM. Основная цель состоит в том, чтобы избежать «внедрения» какого-либо конкретного ORM в ваше решение за пределами его уровня данных. Например, может возникнуть соблазн создать строки HQL ( Hibernate Query Language ) на бизнес-уровне. Однако это, казалось бы, невинное решение связало бы ваш бизнес-уровень с Hibernate, что позволило бы объединить бизнес и уровни доступа к данным; Вы должны стараться избегать этой ситуации в максимально возможной степени.

РЕДАКТИРОВАТЬ: В вашем случае дополнительный слой внутри хранилища является пустой тратой времени: исходя из вашей второй точки, ваш бизнес-уровень достаточно изолирован от вашего хранилища. Обеспечение дополнительной изоляции внесло бы ненужную сложность, без дополнительных преимуществ.

Проблема с созданием дополнительного уровня абстракции в вашем репозитории заключается в том, что конкретный «бренд» ORM определяет, как вы взаимодействуете с ним. Если вы создаете тонкую оболочку, которая выглядит как ваш ORM, но находится под вашим контролем, заменить базовый ORM будет примерно так же сложно, как и без этого дополнительного слоя. Если, с другой стороны, вы создаете слой, который не похож на ваш ORM, вам следует усомниться в выборе технологии объектно-реляционного отображения.

dasblinkenlight
источник
Я так рад, что .NET решил эту проблему, запустив Query Object в платформу. Порт .NET Hibernate даже поддерживает его.
Майкл Браун
2
@MikeBrown Да, и .NET также поставили две собственные конкурирующие технологии ORM, обе с использованием технологии LINQ!
dasblinkenlight
@dasblinkenlight Я обновил вопрос, чтобы дать вам дополнительную информацию.
Патрик Дежарден
В последнее время я принял подход, при котором бизнес-уровень зависит от уровня данных, основанного на интерфейсах типа карты и набора типа для хранения состояния. Теперь я считаю, что эти интерфейсы достаточно хорошо отражают проблему сохранения состояния (вдохновленную функциональным стилем программирования) и обеспечивают хорошее отделение от ORM по выбору благодаря довольно тонкой реализации.
белучин
2

UnitOfWork обычно предоставляет эту абстракцию. Это одно место, которое нужно изменить, ваши репозитории зависят от него через интерфейс. Если вам когда-нибудь понадобится изменить O / RM, просто внедрите новый UoW поверх него. Один и готово.

Кстати, это выходит за рамки простого переключения O / RM, подумайте о модульном тестировании. У меня есть три реализации UnitOfWork, одна для EF, одна для NH (потому что на самом деле мне пришлось переключить промежуточный проект O / RM для клиента, которому нужна поддержка Oracle), и одна для персистентности InMemory. Постоянство InMemory идеально подходило для модульного тестирования или даже для быстрого создания прототипов, прежде чем я был готов поставить за собой базу данных.

Фреймворк прост в реализации. Сначала у вас есть общий интерфейс 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);

}

И интерфейс 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;
}

Далее идет базовый репозиторий (ваш выбор - должен ли он быть абстрактным

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

Реализация IUnitOfWork - это детская игра для EF, NH и In Memory. Причина, по которой я возвращаю IQueryable, заключается в том, что по той же причине, как сказал Айенде в своем посте, клиент может дополнительно фильтровать, сортировать, группировать и даже проецировать результат, используя LINQ, и вы все равно получаете преимущество от того, что все это выполняется на стороне сервера.

Майкл Браун
источник
1
Но вопрос здесь в том, чтобы определить, полезен ли этот уровень выше и должен ли он быть привратником для всего доступа к данным.
Брайан
Хотелось бы, чтобы я указывал на мой пост в блоге о реализации Unit of Work / Repository. В нем обсуждаются точные проблемы из ОП.
Майкл Браун
Присвоение имени слою не означает, что это необходимо или полезно.
Кевин Клайн
Обратите внимание, согласно ОП, у него есть дополнительное отображение между доступом к данным и бизнес-уровнем. Для меня мои бизнес-объекты и объекты сущностей одинаковы. EF и NH предоставляют удивительные API сопоставления, так что сопоставление данных редко (если вообще когда-либо) становится проблемой.
Майкл Браун
Как вы переводите произвольное выражение в эффективный вызов ORM? Вы не можете просто извлечь все и выбросить строки, которые не соответствуют фильтру.
Кевин Клайн