Использование моделей View-Models с шаблоном репозитория
Я использую архитектуру N-многоуровневого приложения с доменными именами с EF code first
в моем недавнем проекте, я определил свои контракты Repository
В слое Domain
.
Основной контракт, чтобы сделать другое Repositories
менее подробным:
public interface IRepository<TEntity, in TKey> where TEntity : class
{
TEntity GetById(TKey id);
void Create(TEntity entity);
void Update(TEntity entity);
void Delete(TEntity entity);
}
И специализированный Repositories
за каждый Aggregation root
, например:
public interface IOrderRepository : IRepository<Order, int>
{
IEnumerable<Order> FindAllOrders();
IEnumerable<Order> Find(string text);
//other methods that return Order aggregation root
}
Как вы видите, все эти методы зависят от Domain entities
.
Но в некоторых случаях приложению UI
нужны некоторые данные, которые не являются Entity
, данные могут быть сделаны из двух или более данных энтерита (View-Model
s), в этих случаях я определяю View-Model
в Application layer
, поскольку они тесно зависят от потребностей Application's
, а не от Domain
.
Итак, я думаю, что у меня есть 2 способа показать данные как View-Models
в UI
:
- Оставьте специализированный
Repository
только Entities
и сопоставьте результаты метода Repositories
с View-Models
, когда я хочу показать пользователю (обычно Application layer
).
- Добавьте несколько методов к моей специализированной
Repositories
, которые возвращают их результаты как View-Models
напрямую, и используют эти возвращаемые значения в Application layer
, а затем UI
(эти специализированные контракты Repositories
, которые я называю их Readonly Repository Contract
s, введите Application layer
в отличие от другого контракта Repositories
'e, который помещается в Domain
).
![enter image description here]()
Предположим, my UI
нуждается в View-Model
с 3 или 4 свойствами (от 3 или 4 большой Entities
).
Эти данные могут быть сгенерированы с простой проекцией, но в случае 1, поскольку мои методы не могли получить доступ к View-Models
, я должен получить все поля из всех 3 или 4 таблиц, иногда с огромными объединениями, а затем сопоставьте результаты с View-Models
.
Но в случае 2 я мог просто использовать проекцию и непосредственно заполнить View-Model
.
Итак, я думаю, что с точки зрения производительности случай 2 лучше, чем случай 1. но я читал, что Repository
должен зависеть от Entities
, а не View-Models
в проектной точке.
Есть ли лучший способ, который не приводит к тому, что уровень Domain
зависит от Application layer
, а также не влияет на производительность? или допустимо, что для чтения запросов мой Repositories
зависит от View-Models
? (case2)
Ответы
Ответ 1
Возможно, использование разделения командной строки (на уровне приложения) может немного помочь.
Вы должны сделать ваши репозитории зависимыми только от сущностей, и сохраните только тривиальный метод извлечения, то есть GetOrderById(), в вашем репозитории (вместе с созданием/обновлением/слиянием/удалением, конечно). Представьте себе, что сущности, репозитории, службы домена, команды пользовательского интерфейса, службы приложений, которые обрабатывают эти команды (например, определенный веб-контроллер, который обрабатывает запросы POST в веб-приложении и т.д.), Представляют вашу запись модель, сторона записи вашего приложения.
Затем создайте отдельную прочитанную модель, которая может быть такой же грязной, как вы пожелаете, - добавьте туда 5 таблиц, код, который читает из файла число звезд во Вселенной, умножает его с количеством книг, начиная с A (после выполнения запроса против Amazon) и создает n-мерную структуру, которая и т.д. - вы получаете идею:) Но, на модели чтения, не добавляйте код, который имеет дело с изменяя ваши сущности. Вы можете возвращать любые модели просмотра, которые вы хотите, из этой модели чтения, но активируете любые изменения данных здесь.
разделение чтения и записи должно уменьшить сложность программы и сделать все более управляемым. И вы также можете увидеть, что он не нарушит правила дизайна, о которых вы упоминали в своем вопросе (надеюсь).
С точки зрения производительности, используя прочитанную модель, то есть написание кода, который читает данные отдельно от кода, который пишет/изменяется, так как вы можете получить:) Это связано с тем, что вы даже можете калечить некоторый код SQL там, где не спал плохо ночью, - и SQL-запросы, если они хорошо написаны, значительно повысят скорость вашего приложения.
Nota bene: я немного шутил о том, что и как вы можете закодировать свою сторону чтения - код на стороне чтения должен быть как чистый и простой, как код на стороне записи, конечно: )
Кроме того, вы можете избавиться от общего интерфейса репозитория, если хотите, поскольку он просто загромождает домен, который вы моделируете, и заставляет каждый конкретный репозиторий выставлять методы, которые не нужны:) См. это. Например, весьма вероятно, что метод Delete() никогда не будет использоваться для OrderRepository - поскольку, возможно, Заказы никогда не должны удаляться (конечно, как всегда, это зависит). Конечно, вы можете поддерживать примитивы управления строкой базы данных в одном модуле и повторно использовать эти примитивы в своих конкретных хранилищах, но не подвергать эти примитивы кому-либо другому, кроме реализации репозиториев, просто потому, что они больше не нужны нигде и может запутать пьяного программиста, если его публично раскрыли.
Наконец, возможно, было бы неплохо не думать о слое уровня домена, слое приложений, слое данных или просмотре моделей слишком строгим образом. Пожалуйста, прочитайте это. Упаковка ваших программных модулей по их реальному значению/назначению (или функции) немного лучше, чем упаковка их на основе неестественный, трудно понятный, труднообъяснимый-к-5-old-kid критерий, то есть упаковка их по уровню.
Ответ 2
Хорошо, для меня я бы привязал объекты ViewModel к объектам модели и использовал их в своих репозиториях для чтения/записи, так как вы можете знать, что есть несколько инструментов, которые вы можете сделать для этого, в моем личном случае я использую automapper, который я нашел очень простым в реализации.
попытайтесь сохранить зависимость между уровнем веб-уровня и уровнями репозитория как можно более разным, говоря, что репозитории должны говорить только с моделью, а ваш веб-слой должен разговаривать с вашими моделями просмотров.
Опция может заключаться в том, что вы можете использовать DTO в службе и автоматизировать эти объекты на веб-уровне (возможно, это может быть случайное сопоставление), недостатком является то, что вы можете получить много шаблонов код и модели dtos и view могут быть дублированы.
другой вариант - вернуть частичные гидратированные объекты в вашу модель и выставить эти объекты как DTO и сопоставить эти объекты с вашими моделями просмотров, это решение может быть немного неясным, но вы можете сделать прогнозы, которые хотите, и вернуть только информацию что вам нужно.
вы можете избавиться от моделей просмотра и выставить dtos в своем веб-слое и использовать их в качестве моделей просмотра, меньше кода, но более сближенного подхода.
Ответ 3
I Kindof согласен с Педро здесь. использование уровня сервиса приложения может быть benificial. если вы нацелились на реализацию типа MVVM, я бы посоветовал создать класс Model, который отвечает за хранение данных, которые извлекаются с использованием уровня сервиса. Сопоставление данных с automapper - это действительно хорошая идея, если ваши сущности, DTO и модели названы последовательно (так что вам не нужно писать много ручных сопоставлений).
В моем опыте использования ваших сущностей /poco в режимах просмотра для отображения данных вы получите большие шарики грязи. Различные представления имеют разные потребности, и все это добавит необходимость добавления дополнительных свойств для объекта. Медленно делая ваши запросы более сложными и медленными.
если ваши данные не изменяются, что часто вы можете рассмотреть возможность представления (sql/database), которые будут переносить некоторые тяжелые нагрузки в базу данных (там, где она сильно оптимизирована). EF достаточно хорошо обрабатывает представления базы данных. Затем получение объекта и отображение данных (из представлений) на модель или DTO становится довольно простым.