Должны ли написать сложный запрос в хранилище или слой службы? - PullRequest
0 голосов
/ 13 мая 2018

Я планирую перенести наш уровень доступа к данным с использованием шаблона хранилища и единицы работы.

Я знаю, что репозиторий поможет мне легко изменить постоянное хранилище (базу данных, коллекцию и т. Д.) И технологию, такую ​​как EF, на MongoDB. Поэтому я заметил несколько ключевых моментов реализации репозитория, таких как:

  1. Возврат IEnumerable вместо IQueryable
  2. Репозиторий должен нести ответственность только за операции CRUD
  3. Возвращаемый тип метода хранилища должен быть модель (сущность)
  4. Реализовать репозиторий только для совокупного корня

Если я применяю эти ключевые моменты во время реализации репозитория в моем проекте, я полностью теряю, как справляться со сложным запросом, в котором связан с несколькими объектами.

В настоящее время у меня уже было то, что в библиотеке BLL с большим количеством классов обслуживания будет напрямую связываться с DbContext и DbSet EF и некоторыми проверками вроде этого:

public IEnumerable<ProjectDTO> GetProjectWithDetails()
{
    // Validation

    // Logging

    // Can be any logic need to before query data.  

    Dbcontext.Projects.Where(p => 
    // multiple of conditions go here follow business rules
    // conditions will need to check another entities (task, phase, employee...) such as:
    // 1. project have task status 'in-progress' .. etc
    // 2. project have employeeid 1,2,3..
    // 3. project have stask start at some specific date.
    // 4....    
    )
    .Select(p => new ProjectDTO
    {
        Label = p.Label,
        Phase = new PhaseDTO{
            Label = p.Phase.Label,
            Tasks = p.Phase.Tasks.Select(t => new TaskDTO{
                // some related properties
            })
        }
    }).ToList();
} 

В настоящее время я использую Data Transfer Object (DTO), чтобы быть средними классами между моделью и viewmodel на контроллере, и использую Mapper для сопоставления свойств.

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

Итак, вопрос: что мне делать в этом случае? Пожалуйста, дайте мне несколько советов, чтобы поставить меня на правильный путь.

Большое спасибо.

Ответы [ 2 ]

0 голосов
/ 26 мая 2018

Одним из преимуществ использования шаблона репозитория является скрытие сложных запросов, вы должны видеть репозиторий как набор объектов в памяти (Мартин Фаулер):

Репозиторий является посредником между доменом и слоями отображения данных, действуя как коллекция объектов домена в памяти. Объекты клиента создают декларативные спецификации запросов и отправляют их в хранилище для удовлетворения. Объекты могут добавляться и удаляться из репозитория, как и из простой коллекции объектов, и код отображения, инкапсулированный репозиторием, будет выполнять соответствующие операции за кулисами. Концептуально репозиторий инкапсулирует набор объектов, сохраняемых в хранилище данных, и операции, выполняемые над ними, обеспечивая более объектно-ориентированное представление уровня персистентности. Репозиторий также поддерживает цель достижения чистого разделения и односторонней зависимости между доменом и слоями отображения данных. https://martinfowler.com/eaaCatalog/repository.html

0 голосов
/ 15 мая 2018

Это зависит от мнения и варианта использования, но я лично не согласен с некоторыми из упомянутых вами ключевых моментов.

Возврат IEnumerable вместо IQueryable

Согласен.Возвращение IQueryable побеждает основную цель существования Репозитория.В сети много статей, объясняющих, как это создает больше проблем, чем решений.Хотя я научился никогда не говорить никогда.См. это , это или это .Или просто выполните поиск google .

Репозиторий должен взять на себя ответственность только за операции CRUD

Согласен.С простым CRUD, он также может выполнять сложные операции чтения и записи.Мой опыт показывает, что в исключительных случаях вы должны поместить часть бизнес-логики в хранилище, если вы хотите реализовать ее на стороне СУБД.Это не правильно или неправильно.Если вы знаете, что делаете, проблем не должно быть.

Возвращаемый тип метода репозитория: модель (сущность)

Если вы не используете DDD, тогда да.В противном случае это решение о реализации.При использовании полной ORM, такой как EF или NHibernate, лучше возвращать модель домена напрямую, а не для каждого экземпляра таблицы Entity.

Всегда рекомендуется, чтобы репозиторий возвращал модель домена.Таким образом, отображение данных, возвращаемых из RDBMS с помощью модели предметной области (и наоборот), становится обязанностью репозитория.Это избавляет от необходимости вытекать проблемы постоянства за пределы репозитория и, таким образом, делает вашу постоянную целостность приложения невежественной.

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

Все это зависит от вашей проблемы, ваших целей проектирования, размера приложения и других реализованных шаблонов проектирования и т. Д. Вот почемуэто становится проектным решением.

Реализуйте репозиторий только для объединенного корня

Согласовано, если это с DDD.Если нет, доступно несколько вариантов, например, для каждого хранилища таблиц.Опять же, зависит от варианта использования.

О сложном запросе

Нет необходимости в том, чтобы в репозиториях использовались только простые методы CRUD.Также может возвращать сложный граф объектов.Это может сделать сложные запросы.Тем не менее, с простыми методами, такими как Get, GetById и т. Д., Он также может использовать сложные методы, такие как GetTopBrokenVehicles(vehicleType, top).Это совершенно нормально, если вы пишете отдельный метод для сложного запроса.

Проблема заключается в том, как вы принимаете необходимые параметры.Вы можете принять параметры встроенными или создать отдельный простой класс входных параметров.

Вот пример кода для Хранилище и UoW .

...