Использовать объект значения в команде и событии?

Можем ли мы использовать объект значения в команде?

Предположим, у меня есть Магазин (агрегат), в котором есть одно значение объекта Address. В конструкторе объекта-значения Address я поместил некоторую логику проверки для адреса. Итак, если я использую этот объект Address в команде (CreateShopCmd), тогда он проверяется при создании команды, но то, что я хочу или прочитал, эта проверка должна присутствовать в обработчике команд.

Но проблема в том, что я должен снова поместить эту проверку в обработчик команд (поскольку проверка уже присутствует в конструкторе адреса), и если я не помещаю это в обработчик команд, тогда проверка будет происходить, когда я создаю объект Address в обработчик событий и назначить его агрегату Shop (что неверно)

Итак, пожалуйста, наставьте меня.

Ниже приведен пример кода

   @Aggregate
   @AggregateRoot
   public class Shop {

   @AggregateIdentifier
   private ShopId shopId;
   private String shopName;
   private Address address;

   @CommandHandler
   public Shop(CreateShopCmd cmd){

     //Validation Logic here , if not using the Address in 
     // in cmd

         //Fire an event after validation
         ShopRegistredEvt shopRegistredEvt = new ShopRegistredEvt();
         AggregateLifecycle.apply(shopRegistredEvt);
     }

     @EventSourcingHandler
     public void on(ShopRegistredEvt evt) {

     this.shopName = evt.getShopName();

     //Validation happend here if not put in cmd at the time of making 
     //Address object - this is wrong
     this.address = new Address(evt.getCity(),evt.getCountry(),evt.getZipCode())

     }


   }

  public class CreateShopCmd{

    private String shopId;
    private String shopName;
    private String city;
    private String zipCode;
    private String country;

   }

 public ShopCreatedEvent{

    private String shopId;
    private String shopName;
    private String city;
    private String zipCode;
    private String country;

}

person Ashwani Tiwari    schedule 09.05.2019    source источник


Ответы (5)


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

person DmitriBodiu    schedule 10.05.2019

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

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

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

person Allard    schedule 10.05.2019

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

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

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

В вашем дизайне нет ничего плохого, это на самом деле то, как Вон Вернон (автор книги «Реализация дизайна, основанного на домене» - IDDD) сделал в своем репозитории, вы можете проверить уровень приложения по этой ссылке:

https://github.com/VaughnVernon/IDDD_Samples/blob/master/iddd_identityaccess/src/main/java/com/saasovation/identityaccess/application/IdentityApplicationService.java

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

@Transactional
public void changeUserContactInformation(ChangeContactInfoCommand aCommand) {
    User user = this.existingUser(aCommand.getTenantId(), aCommand.getUsername());

    this.internalChangeUserContactInformation(
            user,
            new ContactInformation(
                    new EmailAddress(aCommand.getEmailAddress()),
                    new PostalAddress(
                            aCommand.getAddressStreetAddress(),
                            aCommand.getAddressCity(),
                            aCommand.getAddressStateProvince(),
                            aCommand.getAddressPostalCode(),
                            aCommand.getAddressCountryCode()),
                    new Telephone(aCommand.getPrimaryTelephone()),
                    new Telephone(aCommand.getSecondaryTelephone())));
}

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

person Sylvain Lecoy    schedule 14.05.2019
comment
да, вы правы, но если вы видите в моем коде, я создаю объект значения (логика проверки, присутствующая в конструкторе объекта значения) в методе обработки событий. Таким образом, проверка выполняется после обработчика команды. Но, как я знаю, проверка должно выполняться в обработчике команд, а не в обработчике событий. Как есть предложения по этому поводу? - person Ashwani Tiwari; 17.05.2019
comment
Помните, что команды могут быть отклонены, событие - нет, событие уже произошло в прошлом, поэтому вы правы, когда говорите, что проверка не может быть выполнена в обработчике событий. Вам нужно поместить VO в событие, чтобы обработчик команд завершился ошибкой, когда он попытается создать и отправить допустимое событие. - person Sylvain Lecoy; 17.05.2019

Краткий ответ: Вы когда-нибудь думали о Integer, String, Boolean и т. д.? Это тоже объекты-ценности. Единственная разница в том, что вы не создавали их сами. Теперь попробуйте создать команду без каких-либо объектов значений ;-)

Длинный ответ: в целом я не вижу проблем с объектами значений в командах. Если вы следуете нескольким простым рекомендациям:

Самый важный код в вашем приложении - это ваша модель домена. Модель предметной области определяет структуры данных, которые она ожидает для обработки команд. Это означает: Единственная причина изменить вашу Командную Модель - это если ваша Доменная Модель требует этого изменения. То же самое относится к вашим объектам-значениям: объекты-значения изменяются только в том случае, если это изменение требуется вашей моделью предметной области. Без исключений!

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

Как сказано выше: целые числа и строки также являются объектами значений. Если вы используете в своей команде только базовые типы, она уже вызовет исключение, если вы попробуете new SetAgeCommand(aggId, "foo"), потому что String cannot be assigned to int. То же самое применимо, если вы не укажете агрегированный идентификатор для своего UpdatePersonCommand. Это никакие бизнес-ограничения, а вместо этого очень простые данные и проверка типа. Ваша команда никогда не будет создана, если вы передадите искаженные данные.

Теперь предположим, что у вас есть объект PersonAge Value. Неважно, где вы создаете этот объект, потому что в любом случае он должен вызвать исключение, если вы попытаетесь построить его с отрицательным числом: -5 cannot be assigned to PersonAge - выглядит знакомо? Если вы можете быть уверены, что ваш код создал эти экземпляры объекта значения, вы можете точно знать, что они действительны.

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

И пару слов о событиях: я бы посоветовал использовать только очень простые объекты-значения внутри ваших событий. Например: String, Integer, Date и т. Д. Практически все типы объектов значений, которые никогда не изменятся. Причина: требования бизнеса могут измениться. Например: возможно, ваша модель предметной области требует изменения вашего Address объекта-значения, и теперь он требуется для предоставления географических координат. Тогда это неявно изменит ваш NewAddressAddedEvent. Но у ваших уже сохраненных событий не было этого требования, хотя вы не можете создавать Address объекты значений из ваших прошлых данных события, потому что новый объект Address Value вызовет исключение, если не указаны географические координаты.

Есть (как минимум) два решения этой проблемы:

  1. Версионные события: после изменения вашего Address объекта-значения у вас есть NewAddressAddedEvent_Version2, который использует новый Address объект-значение, и у вас есть старый NewAddressAddedEvent, который должен использовать резервную копию старого Address объекта-значения.
  2. Напишите сценарий, который «ремонтирует» вашу базу данных событий, добавляя географические координаты к каждому событию, использующему объект Address Value. Так что можете выбросить старый NewAddressAddedEvent.
person Benjamin M    schedule 14.11.2019

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

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

person Mohsen    schedule 08.03.2020