Просьба пояснить, как создается/обновляется с дочерними объектами совокупного корня

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

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

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

Рассмотрим страховой полис автомобиля (я не в страховании, но это соответствует языку, который я слышу, когда на телефоне с моей страховой компанией).

Политика - это, очевидно, субъект. В рамках политики, допустим, у нас есть Авто. Авто, ради этого примера, существует только в пределах политики (возможно, вы могли бы перенести Auto на другую политику, так что это также возможно для агрегата, что изменяет Политику... но предположим, что это проще, чем сейчас), Поскольку Auto не может существовать без Политики, я думаю, что это должен быть Entity, но не корень. Таким образом, политика в этом случае является совокупным корнем.

Теперь, чтобы создать Политику, предположим, что она должна иметь как минимум один авто. Здесь я расстраиваюсь. Предположим, что Auto довольно сложный, в том числе много полей и, возможно, ребенок, где он находится в гараже (местоположение). Если я правильно понял, конструктор "create Policy" /factory должен был бы принимать в качестве входного параметра Auto или быть ограниченным через построитель, который не будет создан без этого Авто. И автосоздание, поскольку оно является сущностью, не может быть сделано заранее (потому что оно неизменное, может быть, это просто некорректная интерпретация). Таким образом, вы не можете сказать новый Auto, а затем setX, setY, add (Z).

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

Еще один поворот к этому - позже, после создания Политики, и вы хотите добавить еще один Авто... или обновить существующий Авто. Ясно, что политика контролирует это... отлично... но Policy.addAuto() не будет летать, потому что нельзя просто перейти в новый Auto (правый!?). Например, такие вещи, как Policy.addAuto(VIN, make, model и т.д.), Но все так просто, что это выглядит разумно. Но если этот метод метода factory разваливается со слишком большим количеством параметров (возможно, весь автоматический интерфейс), мне нужно решение.

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

auto = AutoFactory.createAuto(); auto.setX auto.setY

или, если придерживаться неизменяемости, AutoBuilder.new(). setX(). setY(). build()

а затем выясните, когда вы произнесите команду Policy.addAuto(auto)

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

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

Я думаю, что я рядом. Надеюсь, что это ясно, а не просто повторение часто задаваемых вопросов, которые имеют ответы повсюду.

Ответ 1

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

Технически все, что у вас есть, - это объекты-объекты и объекты.

Разница между ними - неизменность и идентичность.

Объект Value должен быть неизменным, а идентификатор - это сумма его данных.

Money // A value object
{
    string Currency;
    long Value;
}

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

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

Person // An entity
{
    PersonId Id; // An immutable Value Object storing the Person unique identity
    string Name;
    string Email;
    int Age;
}

Итак, когда и почему у вас есть сводные корни?

Агрегатные корни являются специализированными сущностями, задачей которых является группирование набора понятий домена в рамках одной транзакционной области с целью изменения данных. То есть, скажем, у Человека есть Ноги. Вам нужно будет спросить себя, должны ли изменения в ногах и изменениях в Личности группироваться вместе по одной транзакции? Или я могу изменить его отдельно от другого?

Person // An entity
{
    PersonId Id;
    string Name;
    string Ethnicity;
    int Age;
    Pair<Leg> Legs;
}

Leg // An entity
{
    LegId Id;
    string Color;
    HairAmount HairAmount; // none, low, medium, high, chewbacca
    int Length;
    int Strength;
}

Если нога может быть изменена сама по себе, и Человек может быть изменен сам по себе, то они оба являются Агрегатными Корнями. Если Нога не может быть изменена в одиночку, и Лицо должно всегда участвовать в транзакции, то Должна быть составлена ​​в объекте Person. В этот момент вам придется пройти через Лицо, чтобы изменить Ногу.

Это решение будет зависеть от модели, которую вы моделируете:

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

Или вы можете быть в области археологии. Здесь вы найдете Ноги, и вы можете самостоятельно манипулировать Ногами. В какой-то момент вы можете найти полное тело и угадать, кто этот человек был исторически, теперь у вас есть Человек, но человек не имеет права говорить, что вы будете делать с Ногами, которые вы нашли, даже если это было показано Ноги. Цвет ноги изменяется в зависимости от того, сколько реставрации вы применили к нему, или по другим вещам. Эти инварианты будут поддерживаться другой Сущностью, на этот раз это будет не Человек, а, может быть, археолог.

ОТВЕТЬТЕ ВАШ ВОПРОС:

Я постоянно слышу, как вы говорите об Auto, так что, очевидно, важная концепция вашего домена. Является ли это объектом или значением объекта? Имеет ли значение, если Auto - это номер с серийным номером #XYZ, или вас интересуют только бренд, цвет, год, модель, марка и т.д.? Скажите, что вам небезразлична точная идентификация Авто, а не только его функций, чем это должно было быть сущностью вашего домена. Теперь вы говорите о Политике, политика диктует, что покрывается и не покрывается Авто, это зависит от самого Авто и, возможно, от Клиента, поскольку в основе его истории вождения относятся тип и год, а что нет. имеет, его политика может отличаться.

Поэтому я уже могу представить, что:

Auto : Entity, IAggregateRoot
{
    AutoId Id;
    string Serial;
    int Year
    colour Colour;
    string Model
    bool IsAtGarage
    Garage Garage;
}

Customer : Entity, IAggregateRoot
{
    CustomerId Id;
    string Name;
    DateTime DateOfBirth;
}

Policy : Entity, IAggregateRoot
{
    string Id;
    CustomerId customer;
    AutoId[] autos;
}

Garage : IValueObject
{
    string Name;
    string Address;
    string PhoneNumber;
}

Теперь, как вы это делаете, вы можете изменить политику, не меняя вместе Auto и Customer. Вы говорите такие вещи, как, если Авто находится в гараже, или мы передаем Авто из одной Политики в другую. Это заставляет меня чувствовать, что Auto - это собственный Aggregate Root, а также Policy, а также Customer. Почему это? Потому что это звучит так, как это использование вашего домена, что вы измените автогараж, не заботясь о том, чтобы с ним изменилась политика. То есть, если кто-то изменяет состояние Auto Garage и IsAtGarage, вам не нужно менять политику. Я не уверен, если я буду чист, вы не захотите менять имя клиента и DateOfBirth не транзакционным способом, потому что, возможно, вы измените его имя, но оно не изменит дату, и теперь у вас есть коррумпированный клиент, чья дата рождения не совпадает с его именем. С другой стороны, прекрасно менять Auto без изменения Политики. Из-за этого Auto не должен находиться в совокупности политики. Фактически, Auto не является частью политики, а только тем, что политика отслеживает и может использовать.

Теперь мы видим, что тогда совершенно очевидно, что вы можете создать Auto самостоятельно, поскольку это Aggregate Root. Точно так же вы можете создавать Клиенты самостоятельно. И когда вы создаете политику, вы просто должны привязать ее к соответствующему клиенту и его авторам.

aCustomer = Customer.Make(...);
anAuto = Auto.Make(...);
anotherAuto = Auto.Make(...);
aPolicy = Policy.Make(aCustomer, { anAuto, anotherAuto }, ...);

Теперь, в моем примере, Garage не является Агрегированным корнем. Это связано с тем, что с ним напрямую не работает домен. Он всегда используется через Авто. Это имеет смысл, страховые компании не владеют гаражами, они не работают в бизнесе гаражей. Вам никогда не понадобилось бы создавать Гараж, который существовал бы на нем. Легко тогда иметь метод anAuto.SentToGarage(name, address, phoneNumber) в Auto, который создает Garage и назначает его Auto. Вы не захотите удалить свой собственный гараж. Вместо этого вы бы сделали anAuto.LeftGarage().

Ответ 2

сущности ниже корня агрегата должны быть неизменными.

Нет. Объекты ценности должны быть неизменными. Сущности могут изменять свое состояние.

Просто убедитесь, что вы делаете правильную инкапсуляцию:

  • сущности модифицируют себя
  • сущности изменяются только с помощью только агрегатного корня

но Policy.addAuto() не будет летать, потому что нельзя просто передать новый Auto (правый!?)

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


Однако "реальный мир" не работает таким образом, и я чувствую вашу боль.

Я получил случай, когда пользователь загружает 18 листов excel длинной дерьмовой загрузки данных (с дополнительным причудливым правилом - он должен быть "импортирован" независимо от того, как недействительные данные (как я говорю - это, как сказать true = false)). Этот процесс загрузки рассматривается как одна атомная операция.

Что я делаю в этом случае...

Прежде всего - у меня есть объектная модель excel, сопоставления (например, Customer.Name == 1st sheet, "C24" ) и считыватели, которые заполняют DOM. Эти вещи живут в инфраструктуре далеко.

Следующее - объекты объекта и значения в моем домене, которые похожи на DOM dto`s, но только проекция, которая меня интересует, с правильными типами данных и в соответствии с валидацией. + У меня есть ассоциация 1:1 в моей модели домена, которая изолирует грязные беспорядки (к счастью, она похожа на вездесущий язык).

Вооружившись этим - осталось еще одна сложная часть - сопоставление между excel DOM dtos для объектов домена. Здесь я жертвую инкапсуляцией - я строю объект со своими объектами значения извне. Мой процесс мышления прост - этот переопределенный объект не может быть сохранен в любом случае, и действительность все равно может быть принудительно (через конструкторы). Он живет под совокупным корнем.

В принципе - это та часть, в которой вы не можете убежать от CRUDyness.
Иногда приложение просто редактирует кучу данных.

P.s. Я не уверен, что я поступаю правильно. Вероятно, я пропустил что-то важное в этом вопросе. Надеюсь, что у других ответчиков будет некоторое понимание.

Ответ 3

Часть моего ответа, похоже, зафиксирована в этих сообщениях:

Проект, управляемый доменом - шаблон родительского отношения - шаблон спецификации

Лучшая практика для обработки родительских дочерних коллекций NHibernate

как добавить объект в коллекцию, поддерживаемую агрегированным корнем

Подводя итог:

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

Я вижу свои альтернативы как:

(a) Сначала создайте DTO или другой анемичный граф, а затем передайте его factory, чтобы получить агрегат.

Что-то вроде:

autoDto = new AutoDto();
autoDto.setVin(..);
autoDto.setEtc...
autoDto.setGaragedLocation(new Location(..));
autoDto.addDriver(...);
Policy policy = PolicyFactory.getInstance().createPolicy(x, y, autoDto);
auto1Dto...
policy.addAuto(auto1Dto);

(b) Использовать сборщики (потенциально сложные):

builder = PolicyBuilder.newInstance();
builder = builder.setX(..).setY(..);
builder = builder.addAuto(vin, new Driver()).setGaragedLocation(new Location());
Policy = builder.build();
// and how would update work if have to protect the creation of Auto instances?
auto1 = AutoBuilder.newInstance(policy, vin, new Driver()).build();
policy.addAuto(auto1);

Поскольку эта вещь крутится вокруг и вокруг пары, вещи кажутся ясными.

В духе вездесущего языка имеет смысл сказать:

policy.addAuto

и

policy.updateAuto

Аргументы к этим и тому, как управляются семантика создания совокупности и сущности, не совсем ясны, но необходимость взглянуть на factory для понимания домена кажется немного вынужденным.

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

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

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