Шаблон репозитория без объекта домена для получения скалярных значений

Недавно я изо всех сил пытался выяснить, были ли некоторые методы и функции в моей архитектуре на своем месте; в правильном слое. Я хочу следовать шаблону репозитория Мартина Фаулера, чтобы отделить мой уровень обслуживания от базы данных.

Определение, объясненное Мартином Фаулером на его веб-сайте, гласит:

Репозиторий является посредником между уровнями отображения домена и данных, действуя как коллекция объектов домена в памяти.

Проблема в том, что у меня много требований, по которым я должен, например, получить сумму всех счетов. Как вы понимаете, сумма всех счетов - это скалярная величина. Если я следую определению Мартина Фаулера, то что мне нужно сделать, так это создать функцию, которая возвращает список объектов домена из моего уровня репозитория, который получает уровень сервиса, который циклически выбрасывает объекты для вычисления итога. Также возможно, что я не понимаю сути того, что означает этот паттерн ...

Я знаю, что производительность не должна быть проблемой при разработке приложения, потому что ремонтопригодность лучше, но в этом случае, на мой взгляд, полная трата времени на разработку и производительность не создавать функцию в репозитории слой, который возвращает десятичное значение, соответствующее общей сумме счетов-фактур, и возвращает это же значение из уровня обслуживания. Материализовать список объектов и затем просто использовать одно свойство - излишество, даже если вы можете добавить некоторую стратегию ленивой загрузки с помощью ORM.

Правильно ли возвращать скалярное значение из уровня репозитория или я должен сопротивляться искушению и всегда возвращать объект (ы) домена с этого уровня и обрабатывать всю бизнес-логику на уровне обслуживания?

У меня также есть много мест, где мои докладчики вызывают непосредственно мой репозиторий вместо вызова уровня обслуживания, который затем вызывает уровень репозитория. Это верно; Допустимо называть этот шаблон репозитория вне уровня сервиса?

Обратите внимание, что я не хочу возвращать результат IQueryable из моего уровня репозитория, потому что это будет полностью противоречить Закон Деметры.

Кроме того, я не хочу устанавливать запрос непосредственно на моем уровне обслуживания, потому что я хочу иметь возможность полностью модульное тестирование этого уровня; не проводил интеграционный тест.


person Samuel    schedule 24.04.2012    source источник
comment
Я не вижу проблем с возвратом скалярного значения. Что касается вызова репозитория из пользовательского интерфейса, это правильно в некоторых ситуациях (например, заполнение поля со списком). Ничего страшного, если он не был обработан до того, как будет показан. Это только моё мнение   -  person Mathieu    schedule 24.04.2012


Ответы (2)


Проблема легко решается с помощью CQRS (как концепции). Имейте специализированный репозиторий только для запросов, который может использоваться пользовательским интерфейсом (или контроллером при использовании MVC). Это репо может напрямую запрашивать базу данных (объекты ORM, которые на самом деле не имеют значения) и возвращать то, что вам нужно.

Сохраняйте «тяжелые» доменные объекты и соответствующий репозиторий только на время обновления модели.

person MikeSW    schedule 24.04.2012

У меня были похожие проблемы, и в итоге у меня были базовый и производный интерфейсы репозитория. База будет иметь стандартные методы: Fetch, Update и т. Д., В то время как производный интерфейс будет иметь определенные методы (в вашем случае получите скалярное значение).

public interface IRepository { ... }

и

public interface IInvoiceRepository : IRepository 
{ 
  int GetTotal(); 
}

Затем вы создаете класс InvoiceRepository, который реализует интерфейс. Лучше реализовать бизнес-логику в отдельном классе, скажем, InvoiceBusinessObject, который получает инъекцию зависимости типа IInvoiceRepository (используйте параметр в конструкторе). Никогда не используйте реализацию на своем бизнес-уровне.

public class InvoiceBusinessObject
{
  private IInvoiceRepository rep;

  public InvoiceBusinessObject(IInvoiceRepository rep)
  {
    this.rep = rep;
  }

  public int GetTotal()
  {
    return rep.GetTotal();
  }
}

Уровень сервиса может создать экземпляр класса InvoiceRepository и внедрить этот экземпляр в класс бизнес-объекта:

public int GetTotalFromService()
{
  IInvoiceRepository rep = new InvoiceRepository();
  InvoiceBusinessObject bizObj = new InvoiceBusinessObject(rep);
  return bizObj.GetTotal();
}
person dan radu    schedule 24.04.2012
comment
Этот способ кажется очень запутанным IMO, и я не думаю, что для бизнес-объекта уместно скрывать только репозиторий. - person MikeSW; 24.04.2012