Обновить сущность внутри агрегата

Я читал аналогичный вопрос по SO: Как обновить объект внутри Aggregate, но я все еще не уверен, как пользовательский интерфейс должен взаимодействовать с сущностями внутри агрегата.

Допустим, у меня есть User с кучей Addresses. Пользователь - это совокупный корень, в то время как Адрес существует только внутри совокупности.

В веб-интерфейсе пользователь может редактировать свои адреса. В основном происходит следующее:

  • Пользователь видит список адресов в своем веб-интерфейсе.
  • Он нажимает на адрес, и его перенаправляют на эту страницу: edit-address?user=1&address=2
  • На этой странице он получает форму, в которой он может изменить этот адрес.

Я решил обойти совокупный корень, это было бы просто:

  • Мы бы напрямую загрузили Address с его Id
  • Мы обновим его, а затем сохраним

Поскольку мы хотим сделать это способом DDD, у нас есть разные решения:

  1. Либо мы просим пользователя получить этот адрес по идентификатору:

    address = user.getAddress(id);
    address.setPostCode("12345");
    address.setCity("New York");
    em.persist(user);

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

  2. Или мы говорим агрегату обновить существующий адрес:

    user.updateAddress(id, "12345", "New York");
    em.persist(user);

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

  3. Или мы рассматриваем Address как объект значения и не обновляем наш Address, а скорее удаляем его и воссоздаем:

    user.removeAddress(id);
    address = new Address();
    address.setPostCode("12345");
    address.setCity("New York");
    user.addAddress(address);
    em.persist(user);

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

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


person BenMorel    schedule 25.08.2011    source источник


Ответы (1)


Нет, вы ничего не упускаете - в большинстве случаев лучшим вариантом будет номер 2 (хотя я бы назвал этот метод changeAddress вместо updateAdress - обновление кажется таким не-DDD), и независимо от того, адрес - это сущность или объект-значение. Используя Повсеместный язык, вы бы предпочли сказать, что Пользователь изменил свой адрес, так что именно так вы должны его моделировать - это метод changeAddress, который решает, обновлять ли свойства (если Address является Entity) или назначать совершенно новый объект (когда это озвучка).

В следующем примере кода предполагается наиболее распространенный сценарий - адрес как VO:

    public void ChangeAddress(AddressParams addressParams)
    {
        // here we might include some validation

        address = new Address(addressParams);

        // here we might include additional actions related with changing address
        // for example marking user as required to confirm address before
        // next billing
    }

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

И да, вы всегда должны выполнять операции со своими объектами, проходя через совокупный корень - причина этого была указана во многих ответах на SO (например, в этом Основной сводный вопрос).

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

Другой пример - для большинства доменов Money будет объектом значения - 10 $ = 10 $, у него нет идентичности, кроме суммы. Однако, если бы вы смоделировали домен, который имеет дело с деньгами на уровне счетов, каждый счет имел бы свой собственный идентификатор (выраженный каким-либо уникальным номером), таким образом, он был бы Entity.

person kstaruch    schedule 25.08.2011
comment
Спасибо. Это действительно имеет смысл, хотя я не уверен, как реализовать метод changeAddress(). Приведенный выше пример является чрезмерным упрощением, и адрес может состоять из дюжины полей, включая название улицы, номер здания, LatLng точку для точного местоположения на карте и т. Д. Что вы предлагаете тогда, поместите все эти параметры в метод аргументы или создать какой-то временный объект из формы (будет ли это тоже объект Address?) и передать его в качестве аргумента этому методу? - person BenMorel; 26.08.2011
comment
Я добавил образец кода для наиболее распространенного сценария. Я бы не стал беспокоиться о количестве полей - DDD на самом деле не о данных, а о связях и сложности управления. - person kstaruch; 26.08.2011