Управляемый доменом дизайн - Совокупные корни - PullRequest
12 голосов
/ 10 июня 2009

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

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

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

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

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


Пример:

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

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

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

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

Спасибо

Ответы [ 3 ]

13 голосов
/ 10 июня 2009

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

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

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

2 голосов
/ 12 июня 2009

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

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

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

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

1 голос
/ 10 июня 2009

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

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)
...