Подтвердить что ты не робот

Проект, управляемый доменом - совокупные корни

Я борюсь с агрегатами и совокупными корнями. У меня есть естественный совокупный корень, который работает примерно на 60% запросов пользователя. То есть эти запросы естественным образом применяются к совокупному корню.

В моей совокупности у меня есть другой объект, который может существовать только как член совокупного корня. Однако пользователям будет рассказано об этом другом объекте объекта. Понятно, что концептуально, чтобы пользователи могли напрямую работать с этим неагрегатным корневым объектом.

Итак, я думаю, у меня есть несколько вариантов:

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

Обратите внимание, что корневой узел верхнего уровня будет содержать коллекцию этого другого объекта.


Пример:

Основной совокупный корень: автомобиль

Второй объект: сиденье (автомобиль имеет 2 или 4 места в зависимости от типа). В моих доменах места могут существовать только как часть автомобиля.

Большинство операций в домене находятся на уровне Car. Таким образом, это будет хороший кандидат для совокупного корня. Однако (и я изо всех сил пытаюсь привести примеры), некоторые операции будут на уровне места, например. SpillCoffee, ChangeFabric, Clean....

Может ли Seat и Car быть совокупными корнями? Или я должен всегда начинать с автомобиля?

Спасибо

4b9b3361

Ответ 1

Идея агрегата - гарантировать согласованность, являясь корнем, ответственным за целостность данных и заставляя инварианты.

Предположим, что существует такое правило, как "Ткань всех мест должна быть одинаковой" или "вы можете разливать только кофе на сиденье, если кто-то внутри машины". Будет намного сложнее обеспечить их соблюдение, клиенты смогут менять ткань отдельно, или эти инварианты должны быть вынуждены выходить за пределы (опасная зона).

ИМХО, если целостность или принуждение инвариантов не является проблемой, то агрегаты на самом деле не нужны. Но, если это необходимо, мой совет - начать все с автомобиля. Но всегда думайте о модели. Если есть такие инварианты, то кто применяет эти инварианты? Затем попробуйте передать эту идею в код, и все должно быть хорошо.

Ответ 2

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

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

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

Второй агрегат был бы в контексте "места", и были бы возможные действия и конфигурации для места в качестве автономного. Пример: ChangeFabric, ColorList. Таким образом, совокупный "автомобиль" имеет "место", но клиенты могут знать место в контексте, который имеет смысл. Это опасно, как сказал Самуэлькаррихо на предыдущем посту. Если изменения между контекстами влияют на целостность домена, вы потеряли всю концепцию совокупности.

Ответ 3

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

public class Cart : IAggregateRoot
{
  public List<LineItem> LineItems {get;}
}

public class LineItems : IAggregateRoot
{
  public List<LineItem> LineItems {get;}
}

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

public class Order : IAggregateRoot
{
  public List<LineItem> LineItems {get;}
}

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

Car GetCarFromSeatID(guid seatID)