2015-11-04 2 views
4

Давайте рассмотрим классический пример контекста блога. В нашем домене мы имеем следующие сценарии: Users может написать Posts. Posts необходимо каталогизировать, по крайней мере, в одном Category. Posts может быть описано с использованием Tags. Users может комментировать Posts.DDD/CQRS: объединение моделей для чтения для требований пользовательского интерфейса

Четыре лица (Post, Category, Tag, Comment) реализованы в виде различных агрегатов из-за I не обнаружили каких-либо правило, что данные сущности должны вмешиваться в другой. Итак, для каждого агрегата у меня будет один репозиторий, который его представляет. Кроме того, каждая совокупность ссылается на других по его идентификатору.

После CQRS, из этого сценария я вычитаюсь типичные случаи использования, которые приводят на командах, такие как WriteNewPostCommand, PublishPostCommand, DeletePostCommand и т.д ... вместе с их соответствующими запросами для получения данных из хранилищ. FindPostByIdQuery, FindTagByTagNameQuery, FindPostsByAuthorIdQuery и т. Д.

В зависимости от того, на каком сайте приложения мы находимся (бэкэнд или фронт), мы будем иметь более или менее сложные запросы. Итак, если мы находимся на первой странице, нам может понадобиться создать некоторые виджеты, чтобы получать последние комментарии, последнее сообщение категории и т. Д. Запросы, которые включают простой объект Query (несколько критериев поиска) и QueryHandler очень простой (один хранилище в зависимости от класса обработчика)

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

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

Моих вопросы:

  • На уровне инфраструктуры, когда мы проектируем репозитории, методы поиска (FindAll, findById, findByCriterias ...), должен вернуть соответствующий объект, ссылающийся на все ассоциации идентификаторов? Я имею в виду, если у a есть метод findPostById(uuid) или findPostByCustomFilter(filter), должен вернуть экземпляр сообщения со ссылкой на все идентификаторы категорий, которые у него есть, все теги id и идентификатор автора, которые у него есть? Или у моего репо есть какой-то метод, который заполняет данный экземпляр почты ассоциациями, которые я хочу?

  • Если я хочу искать сообщения, созданные с 12/12/2014, написанные Джоном, и классифицируются по категориям и тегам «Новости» и «Видео» и «Приключения», а также детали каждого агрегата, как создать мои Query и QueryHandler?

а) Создать Query со всеми моими параметрами (AUTHORNAME, categoriesNames, TagsNames, если на покупке retrive User, Category, Tag ассоциации полной развернутой), а затем его QueryHandler Ансамбля различных моделей чтения в одной. Или...

б) Создайте другой Queries (FindCategoryByName, FindTagByName, FindUserByName), а затем мой веб-контроллер вызывает их для последующего вызова FindPostQuery но теперь передавая ему AuthorID, CategoryID, TagID вернулся из других запросов?

b) решение кажется более чистым, но, похоже, мне дороже.

ответ

1

Из CQRS у вас будет разделенный стек для запросов и команд. Ваш стек запросов должен представлять собой разностный модуль, пространство имен, dll или пакет в вашем проекте.

a) Вы создадите один QueryModel, и эта модель запроса вернет все, что вам нужно. Если вы знакомы с Entity Framework или NHibernate, вы создадите фасад для хранения этих запросов togheter, DbContext или Session.

b) Вы можете создавать эти разделенные запросы, но, повторяя, если вы знакомы с любым ORM, вы должны вернуть набор, который представляет модель, вернуть каждый набор как IQueryable и использовать LET (деревья выражения Linq), чтобы сделать ваш Стек запроса более динамичный. не

Использование Entity Framework и C# для Exemple:

public class QueryModelDatabase : DbContext, IQueryModelDatabase 
{ 
    public QueryModelDatabase() : base("dbname") 
    { 
     _products = base.Set<Product>(); 
     _orders = base.Set<Order>(); 
    } 

    private readonly DbSet<Order> _orders = null; 
    private readonly DbSet<Product> _products = null; 

    public IQueryable<Order> Orders 
    { 
     get { return this._orders.Include("Items").Include("Items.Product"); } 
    } 

    public IQueryable<Product> Products 
    { 
     get { return _products; } 
    } 
} 

Тогда вы должны делать запросы, как вам нужно, и ничего возвращать:

using (var db = new QueryModelDatabase()) 
{ 
    var queryable = from o in db.Orders.Include(p => p.Items).Include("Details.Product") 
        where o.OrderId == orderId 
        select new OrderFoundViewModel 
        { 
         Id = o.OrderId, 
         State = o.State.ToString(), 
         Total = o.Total, 
         OrderDate = o.Date, 
         Details = o.Items 
        }; 
    try 
    { 
     var o = queryable.First(); 
     return o; 
    } 
    catch (InvalidOperationException) 
    { 
     return new OrderFoundViewModel(); 
    } 
} 
3
  • На стороне запроса, не существует юридические лица. Вы можете свободно заполнять свои прочитанные модели любым способом, который наилучшим образом соответствует вашим требованиям. Независимо от того, какие данные вам нужно отображать (часть) экрана, вы помещаете его в считываемую модель. Репликаторы командной строки не возвращают эти модели чтения, а специализированные объекты доступа к данным на стороне запроса.

  • Вы упомянули «сложные критерии поиска» - я рекомендую вам смоделировать его соответствующим объектом SearchCriteria. Этот объект был бы агностически технологичным, но он будет передан вашему объекту доступа к данным Query, который будет знать, как объединить критерии для построения запроса более низкого уровня для конкретного хранилища данных, на которое он нацелен.