Как сохраняется состояние сущности между вызовами EventSourcingHandlers? - PullRequest
0 голосов
/ 04 марта 2019

В демонстрационной версии Axon Giftcard есть класс GiftCard, который аннотируется как @Aggregate:

@Aggregate
@Profile("command")
public class GiftCard {

    private final static Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    @AggregateIdentifier
    private String id;
    private int remainingValue;

    @CommandHandler
    public GiftCard(IssueCmd cmd) {
        log.debug("handling {}", cmd);
        if(cmd.getAmount() <= 0) throw new IllegalArgumentException("amount <= 0");
        apply(new IssuedEvt(cmd.getId(), cmd.getAmount(), cmd.getCurrency()));
    }

    @CommandHandler
    public void handle(RedeemCmd cmd) {
        log.debug("handling {}", cmd);
        if(cmd.getAmount() <= 0) throw new IllegalArgumentException("amount <= 0");
        if(cmd.getAmount() > remainingValue) throw new IllegalStateException("amount > remaining value");
        apply(new RedeemedEvt(id, cmd.getAmount()));
    }

...
    @EventSourcingHandler
    public void on(IssuedEvt evt) {
        log.debug("applying {}", evt);
        id = evt.getId();
        remainingValue = evt.getAmount();
        currency = evt.getCurrency();
        log.debug("new remaining value: {}", remainingValue);
        log.debug("new currency: {}", currency);
    }

   @EventSourcingHandler
    public void on(RedeemedEvt evt) {
        log.debug("applying {}", evt);
        remainingValue -= evt.getAmount();
        log.debug("new remaining value: {}", remainingValue);
    }
...

Классы команд и событий определены в коде Kotlin:

data class IssueCmd(@TargetAggregateIdentifier val id: String, val amount: Int)
data class IssuedEvt(val id: String, val amount: Int)
data class RedeemCmd(@TargetAggregateIdentifier val id: String, val amount: Int)
data class RedeemedEvt(val id: String, val amount: Int)

Допустим, на командную шину помещены следующие две команды:

Command #     Command Class   id          amount
---------     -------------   -------     -------------
1             IssueCmd        QP34        123.45
2             RedeemCmd       QP34        38.10

При обработке первой команды CommandHandler (CH) для IssueCmd будет помещать IssuedEvt объект на шине событий.Событие будет обработано EventSourcingHandler (ESH) для IssuedEvt.Затем у нас будет экземпляр GiftCard с id, установленным на " QP34 " и remainingValue, установленным на 123.45 .

При обработке второгоКоманда CH для RedeemCmd поместит объект RedeemedEvt в шину событий.Событие будет обработано ESH для RedeeemedEvt.Затем у нас будет экземпляр GiftCard с id, установленным на " QP34 " и remainingValue, установленным на 85.35 .

ВОПРОС: После того, как каждое событие обрабатывается назначенным ему ESH, как и где сохраняется полученный экземпляр объекта?

Раньше я слышал ответ: на самом деле это не так.Все, что сохраняется - это объекты событий, которые хранятся в хранилище событий Axon.Когда требуется текущее состояние объекта, Аксон приказывает модели команд инициировать экземпляр класса GiftCard, и к нему применяются события от самого раннего до самого позднего.Это определение Event Sourcing.

Но, когда Event Sourcing, после обработки IssuedEvt, 123.45 в remainingValue должен сохраняться где-то для ESHчтобы RedeemedEvt имел правильное значение для своей операции вычитания.

Как и где сохраняется состояние объекта между вызовами в ESH?

1 Ответ

0 голосов
/ 05 марта 2019

Платформа внутренне создает экземпляр AnnotatedAggregate при извлечении экземпляра Aggregate из Repository.

. Класс AnnotatedAggregate реализует Aggregate, который интерфейс Repository применяет кбыть типом возврата в операции load(String).

Когда вы говорите об источнике событий, используется реализация Repository - EventSourcingRepository, которая в load(String) возвращает EventSourcedAggregateэкземпляр (это реализация AnnotatedAggregate.

Интерфейс Aggregate, реализация AnnotatedAggregate этого интерфейса и реализация EventSourcedAggregate, снова его реализующая, определяют универсальный.

Это универсальный ваш агрегатный вариант реализации.

Когда вы используете Event Sourcing Aggregate через EventSourcingRepository, ваш ваш агрегатный экземпляр хранится в памяти в AnnotatedAggregate под глобальным полем private T aggregateRoot.

Этот aggregateRoot обновляется EventSourcingRepository, который инициализирует состояние вашего EventSourcedAggregate, давая ему поток EventMessages.

Кстати, почему вы заинтересованы в этом еxact bit, @JonathanM?

Для справки, вот ссылки GitHub на классы:

  1. Aggregate
  2. AnnotatedAggregate
  3. EventSourcedAggregate
  4. Repository
  5. EventSourcingRepository
...