Axon - исключение State Stored Aggregates в тесте

Настройка среды: Axon 4.4, H2Database (мы проводим тестирование компонентов в рамках CI). Код выглядит примерно так.

    @Aggregate(repository = "ARepository")
    @Entity
    @DynamicUpdate
    @Table(name = "A")
    @Getter
    @Setter
    @NoArgsConstructor
    @EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
    @Log4j2
    Class A implements Serializable {
    
    
      @CommandHandler
      public void handle(final Command1 c1) {
        apply(EventBuilder.buildEvent(c1));
      }
    
      @EventSourcingHandler
      public void on(final Event1 e1) {
      //some updates to the modela
        apply(new Event2());
      }
    
      @Id
      @AggregateIdentifier
      @EntityId
      @Column(name = "id", length = 40, nullable = false)
      private String id;
    
       @OneToMany(
          cascade = CascadeType.ALL,
          fetch = FetchType.LAZY,
          orphanRemoval = true,
          targetEntity = B.class,
          mappedBy = "id")
      @AggregateMember(eventForwardingMode = ForwardMatchingInstances.class)
      @JsonIgnoreProperties("id")
      private List<C> transactions = new ArrayList<>();
    }
    
    @Entity
    @Table(name = "B")
    @DynamicUpdate
    @Getter
    @Setter
    @NoArgsConstructor
    @EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
    @Log4j2
    Class B implements Serializable {
    
      @Id
      @EntityId
      @Column(name = "id", nullable = false)
      @AggregateIdentifier
      private String id;
    
      @ManyToOne(fetch = FetchType.LAZY)
      @JoinColumns({@JoinColumn(name = "id", referencedColumnName = "id")})
      @JsonIgnoreProperties("transactions")
      private A a;
    
      @EventSourcingHandler
      public void on(final Event2 e2) {
       //some updates to the model
      }
    }

Я использую агрегат хранилища состояний, но постоянно получаю сообщение об ошибке во время Spring Test со встроенным H2. Та же проблема не возникает с базой данных PGSQL в невстроенном режиме, но мы не можем запустить ее в конвейере.

Ошибка: java.lang.IllegalStateException: совокупный идентификатор не был установлен. Он должен быть установлен не позднее, чем при применении события создания

Я прошел через AnnotatedAggregate

    protected <P> EventMessage<P> createMessage(P payload, MetaData metaData) {
            if (lastKnownSequence != null) {
                String type = inspector.declaredType(rootType())
                                       .orElse(rootType().getSimpleName());
                long seq = lastKnownSequence + 1;
                String id = identifierAsString();
                if (id == null) {
                    Assert.state(seq == 0,
                                 () -> "The aggregate identifier has not been set. It must be set at the latest when applying the creation event");
                    return new LazyIdentifierDomainEventMessage<>(type, seq, payload, metaData);
                }
                return new GenericDomainEventMessage<>(type, identifierAsString(), seq, payload, metaData);
            }
            return new GenericEventMessage<>(payload, metaData);
        }

Последовательность для этого устанавливается равной 2 и, следовательно, генерирует исключение вместо ленивой инициализации агрегата.

Что исправить? Мне не хватает какой-то конфигурации или мне нужно исправить код Axon?


person Rohitdev    schedule 27.11.2020    source источник
comment
Не могли бы вы добавить, как вы используете тестовые инструменты Axon для тестирования своих агрегатов? Ожидается, что все будет автоматически подключено к испытательному приспособлению, не ориентированному на пружину, вероятно, виноват.   -  person Steven    schedule 01.12.2020


Ответы (2)


Я считаю, что исключение, которое вы получаете, - это указатель на то, что вам не хватает @Rohitdev. Когда в Axon создается агрегат, предполагается, что вы, как минимум, зададите идентификатор агрегата. Таким образом, вы заполните аннотированное поле @AggregateIdentifier в вашем Aggregate.

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

Из фрагментов кода, которыми вы поделились, нет ничего, что указывало бы на то, что @AggregateIdentifier аннотированные String id поля в Aggregate A или B когда-либо были установлены. Если этого не делать в сочетании с тестовыми приборами Axon, это приведет к исключению, которое вы получите.

При использовании агрегата с сохранением состояния знайте, что вы измените состояние агрегата внутри обработчика команд. Это означает, что после вызова метода AggregateLifecycle#apply(Object) в обработчике команд вы установите id на желаемый совокупный идентификатор.

Есть еще два основных указателя, которыми можно поделиться в зависимости от вопроса. Внутри вашего агрегата нет обработчика команд, который сам создает агрегат. Вы должны либо иметь аннотированный конструктор @CommandHandler в ваших агрегатах, либо использовать аннотацию @CreationPolicy для определения обычного метода в качестве точки создания агрегата (как упоминалось здесь в справочном руководстве).

Наконец, в вашем примере по-прежнему используются @EventSourcingHandler аннотированные функции, которые следует использовать, когда у вас есть агрегат с источником событий. Похоже, вы приняли сознательное решение не использовать Event Sourcing, поэтому я бы не стал использовать эти аннотации в вашей модели. Прямо сейчас разработчиков, скорее всего, только смутит то, что используется сочетание агрегированной логики с хранением в состоянии и на основе событий.

person Steven    schedule 30.11.2020
comment
Стивен, идентификатор установлен. Я пытался создать псевдокод, так как не могу поделиться кодом, над которым мы работаем. Идентификатор установлен, но его нет в прокси-объекте спящего режима, созданном bytebuddy, когда String id = identifierAsString (). Это приведет к тому, что идентификатор будет возвращен как null, чего не должно быть. Когда эта служба тестируется из коллекции почтальона, она работает, но в случае модульных / компонентных тестов с h2db она показывает эту проблему. - person Rohitdev; 30.11.2020
comment
Было бы яснее рассказать о том, как устанавливается идентификатор, особенно когда речь идет об отсутствии идентификатора. Вдобавок к этому было бы полезно рассказать, как вы используете тестовые приборы; вероятно, что-то не так в этой настройке или подготовке к тестированию. - person Steven; 01.12.2020
comment
Он использует Spring MVC Test (Embedded H2), а не тест Axon - person Rohitdev; 02.12.2020
comment
Честно говоря, это только делает его более уверенным, что это не проблема Axon, а проблема конфигурации с вашей стороны ... - person Steven; 07.12.2020
comment
Я бы посмотрел на вашу настройку h2 при тестировании. При необходимости убедитесь, что состояние агрегата действительно сохраняется между тестами. Если это не требуется, обязательно очистите его, прежде чем переходить к следующему тесту. Кроме того, вы утверждаете, что это происходит случайно. Это действительно случайный сбой? Должна быть какая-то эвристика, которая постоянно дает сбой в некоторых сценариях. - person Steven; 07.12.2020

Наконец, после отладки мы обнаружили, что в классе B мы не устанавливали идентификатор для события обновления.

@EventSourcingHandler
 public void on(final Event2 e2) {
    this.id=e2.getId();
 }

Как только мы это сделали, проблема исчезла.

person Rohitdev    schedule 09.12.2020