DDD - Как мне избежать пересечения совокупных границ здесь?

Мы работаем над новым проектом (переписывая существующее приложение), и у меня возникают проблемы с моей моделью предметной области/дизайном репозитория.

Вот (упрощенная) версия двух ключевых частей нашей модели предметной области:

альтернативный текст

Как видите, у меня есть абстрактная концепция публикации, которая может включать в себя такие вещи, как обзор, обсуждение, фото, видео и т. д. Сообщения также могут содержать комментарии.

У меня также есть абстрактное понятие Местоположение, которое, очевидно, представляет собой такие вещи, как улицы, города, районы и т. д.

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

Поэтому я создал два репозитория, один из которых называется PostRepository, а другой называется LocationRepository.

Все это работало нормально, я могу добавить/получить любой тип сообщения (или комментария) и добавить/получить любой тип местоположения через один из этих двух репозиториев.

Но теперь я в сценарии «посадочной страницы» для города (например).

На этой странице мне нужно в основном показать «все сообщения для этого места».

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

Но как я могу получить эту информацию, не пересекая границы?

Какой репозиторий я использую? Мне нужен новый?

Если это имеет значение (или для любопытных), это веб-приложение (ASP.NET MVC) с базой данных SQL Server 2008 и Entity Framework 4.0.

Если вам нужны какие-либо разъяснения, дайте мне знать.

ИЗМЕНИТЬ

В настоящее время мы используем модифицированную версию шаблона спецификации для получения моделей предметной области.

Например, это код в нашем BLL для получения всех отзывов, где оценка >= 4:

var reviews = postRepository // GenericRepository<Post>
      .Find() // IQueryable<Post>
      .OfType<Review>() // IQueryable<Review>
      .Where(x => x.Score >= 4)
      .ToList(); // List<Review>

Но теперь мне нужен такой код:

var reviews = postRepository
    .Find()
    .OfType<Review>()
    .Where( //lat long, or Locations FK )
    .ToList();

Проблема в том, что я не знаю, как выполнить вышеуказанный запрос без добавления промежуточного объекта соединения (LocationPost - как многие ко многим) и добавить к нему FK в модель домена Post.

Но, делая это, я пересекаю совокупные границы, не так ли?


person RPM1984    schedule 26.11.2010    source источник


Ответы (3)


Я бы привязал сообщение к месту во время создания, чтобы для каждого места я мог получить (через репозиторий) список связанных сообщений. Это будет выглядеть так:

Творчество:

var p = new Post(latitude, longitude);
var locations = locationRepository.FindByCoordinates(latitude, longitude);
foreach (var l in locations)
{
    l.AssociatePost(p);
}
session.Save(p);

Поиск:

var associatedPosts = postRepository.FindByLocation(locationId);
foreach (var p in associatedPosts)
{
    Display(p);
}

Под капотом связь между записями и местоположением будет реализована как табличное отношение «многие ко многим». У этого решения есть одна проблема: добавление нового местоположения требует сканирования всех сообщений и назначения их новому местоположению (если применимо).

Надеюсь, это поможет.

person Szymon Pobiega    schedule 26.11.2010
comment
+1, мне это нравится. Я собираюсь попробовать это, когда я вернусь в офис, и я дам вам знать, как я получаю. Ваше здоровье. :) - person RPM1984; 27.11.2010
comment
Я пошел с модифицированной версией этого решения. Ваше общее решение направило меня в правильном направлении, ура. - person RPM1984; 01.12.2010
comment
+1 - Хороший ответ. Для OP, хотя это немного поздно, ваша модель вписывается в классический формат дискуссионного форума, если вы думаете, что место является форумом, а сообщение - самим собой. Вы обнаружите, что это очень распространенный шаблон при работе с совокупными границами и отношениями «один ко многим» и «многие ко многим». - person Joseph Ferris; 14.01.2011

Почему это проблема? Согласно Эвансу в его книге, один AR вполне может ссылаться на другой AR. (Однако вы не можете ссылаться на дочерний элемент в AR из другого AR)

Кроме того, действительно ли местоположения объединяют корни? Определение совокупного корня состоит в том, что он действует как граница согласованности. Соответствует ли это определению местоположения? Я бы сказал, что местоположение — это объект ценности.

Здесь есть два лагеря относительно репозиториев и ассоциаций AR:

Тот, в котором говорится, что все сводные корни должны извлекаться через соответствующий репозиторий, а AR должны использовать мягкие отношения, например идентификаторы между ними.

И тот, который говорит, что совокупные корни вполне могут извлекать другие связанные совокупные корни и что репозиторий — это просто способ найти совокупные корни.

person Roger Johansson    schedule 26.11.2010
comment
Хм, интересует точка RE значение объекта. Но у всех Locations есть идентификатор (LocationId), а также у них есть широта/долгота, которые также уникальны. Но опять же, они неизменны, так что, возможно, они являются объектами-значениями. - person RPM1984; 27.11.2010

Допустим, вы использовали паттерн «Спецификация». Не могли бы вы построить «Спецификацию поста» с использованием объекта «Местоположение»? Затем вы просто передаете спецификацию в свой репозиторий сообщений и получаете результат.

person Scott Whitlock    schedule 26.11.2010
comment
Хм, никогда не слышал о шаблоне критериев. Или вы имеете в виду шаблон спецификации? (мы используем модифицированную версию этого - через выражения С#) - person RPM1984; 26.11.2010
comment
@ RPM1984 RPM1984, не могли бы вы опубликовать этот измененный подход к шаблону спецификации на pastebin/gist и скинуть ссылку сюда? Чую что-то интересное. - person Arnis Lapsa; 26.11.2010
comment
@RPM1984: Вот почему я не смог найти его в Google! :) Ага, это я и имел в виду. - person Scott Whitlock; 26.11.2010
comment
@Arnis L. - рад тебя видеть, у нас будет еще одно жаркое обсуждение DDD? Просто шучу. :) На самом деле, я только что посмотрел видео NDC о 7 причинах, почему проекты DDD терпят неудачу, и это взорвало мне мозг. Ничего особенного в этом коде, я нигде его не нашел - public ICollection<T> FindAll<T>(Expression<Func<T,bool>> predicate) : where T : Post - person RPM1984; 27.11.2010
comment
Это метод службы домена (он же PostServices), который можно использовать следующим образом: var reviews = _postService.FindAll<Review>(x => x.ReviewScore >= 5);. Реализация действует на GenericRepository<T>, используя предикат на .Where. - person RPM1984; 27.11.2010
comment
@ RPM1984 Вы уже знаете - мне не нравится такой подход. Кстати, посмотрите, что такое CQRS. Это поразит Вас еще больше. Просто не пытайтесь делать это дома (в реальном проекте), если только вы не чувствуете себя комфортно. :) bit.ly/3H2WaQ - person Arnis Lapsa; 27.11.2010
comment
@Arnis L - спасибо за ссылки, да, я слышал об этом, но, честно говоря, их довольно много. Думаю, наш проект относится к категории DDD-Lite. Мы действительно не хотим разделять нашу архитектуру на запросы/команды, когда это такое простое приложение. Однако я мог видеть преимущество в других архитектурах (например, N-Tier). - person RPM1984; 27.11.2010
comment
@ RPM1984 О DDD-Lite Думаю, ты прав. @ работаю, ребята рядом со мной делают то же самое. Но я думаю, что это лучше, чем статический веб-шаблон. bit.ly/6fw1Ak Что касается cqrs, то это действительно хорошая тренировка ума. Когда Вы понимаете сложные вещи, менее сложные становятся простыми. - person Arnis Lapsa; 27.11.2010