Невозможно загрузить Aggregate с помощью Repository.load () для Integer AggregateIdentifier - PullRequest
1 голос
/ 08 марта 2020

Я тщетно пытался загрузить агрегат, используя метод load класса репозитория. Я знаю, что метод load () требует строковый аргумент, но мой агрегатный идентификатор целочисленный. Я пытался преобразовать в строку, но я не могу загрузить агрегат. Я продолжаю получать ноль. Мой код показан ниже

    @Autowired
private Repository<Product> repository;

@CommandHandler
public Order(CreateOrderCommand cmd){
    try{
        //This line throws an error
        Product product = (Product) repository.load(Integer.toString(cmd.getProductId()));
    }
    catch (Exception ex){
        System.out.println("Oops! Error Occured: " + ex.getMessage());
    }
    apply(
            new OrderCreatedEvent(
                    cmd.getOrderId(), cmd.getPrice(), cmd.getNumber(), cmd.getProductId()
            )
    );
}

Я предоставляю больше обновлений: я просто хочу обновить ассортимент продукции при размещении нового заказа. Так что я сделал, что в CommandHandler для создания нового ордера я просто нахожу агрегат Product и вызываю его метод UpdateStock, который является обработчиком команд. Есть что-то, чего я действительно не понимаю?

Это файлы ниже: Агрегат продукта:

@Aggregate
public class Product {
    @AggregateIdentifier
    private String id;
    private Double price;
    private Integer stock;
    private String description;

    public Product() {
    }

    @CommandHandler
    public Product(AddProductCommand cmd){
        apply( new ProductAddedEvent(
                cmd.getId(),
                cmd.getPrice(),
                cmd.getStock(),
                cmd.getDescription()
                )
        );
    }

    @CommandHandler
    public void updateStock (UpdateStockCommand cmd){
        if(this.stock >= cmd.getStock()){
            apply(new StockUpdatedEvent(cmd.getId(), cmd.getStock()));
        }
        else {
            throw new RuntimeException("Out of Stock!");
        }
    }

    @EventSourcingHandler
    public void on(StockUpdatedEvent evt){
        id = evt.getId();
        stock = stock - evt.getStock();
    }

    @EventSourcingHandler
    public void on(ProductAddedEvent evt){
        id = evt.getId();
        price = evt.getPrice();
        stock = evt.getStock();
        description = evt.getDescription();
    }
}

Агрегат заказа:

@Aggregate
public class Order {
    @AggregateIdentifier
    private String  orderId;
    private Double price;
    private Integer number;
    private String productId;

    //Repository provides an abstraction for storage of Aggregates
    @Autowired
    private Repository<Product> repository;

    @CommandHandler
    public Order(CreateOrderCommand cmd){
        try{
            //This line throws an error
            Product product = (Product) repository.load(cmd.getProductId());

        }
        catch (Exception ex){
            System.out.println("Oops! Error Occured: " + ex.getMessage());
        }
        apply(
                new OrderCreatedEvent(
                        cmd.getOrderId(), cmd.getPrice(), cmd.getNumber(), cmd.getProductId()
                )
        );
    }

    @EventSourcingHandler
    public void on(OrderCreatedEvent evt){
        orderId = evt.getOrderId();
        price = evt.getPrice();
        number = evt.getNumber();
        productId = evt.getProductId();
    }
}

1 Ответ

1 голос
/ 09 марта 2020

Как вы заметили, Axon принудительно введет String для совокупного идентификатора. Это не значит, что вам нужно предоставить String напрямую. Он будет отлично работать, если у вас есть разумный метод toString(). Следовательно, использование Integer в качестве идентификатора агрегата приведет к результату toString() этого поля, которое будет использоваться в качестве идентификатора агрегата.

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

Кажется, у вас есть обработчик команды конструктора для класса Order. Более того, это сигнализирует мне, что класс Order является агрегатом. Я хотел бы заявить, что никоим образом я бы никогда не рекомендовал загружать в Агрегат из другого Агрегата.

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

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

Какую версию Axon вы используете каким-либо образом? Используете ли вы какие-либо интересные конфигурации вокруг совокупности Product? Вы связываете свое приложение с Axon Server? Если это так, стандарт или предприятие, и какая версия? Вы используете API конфигурации напрямую или axon-spring-boot-starter?


Обновление

Спасибо за обновление вашего вопроса, Киндсон, позвольте мне обновить мой ответ тоже.

Во-первых, как я уже говорил ранее, я бы никогда не загружал бы в Агрегат из другого Агрегата. И ваш класс Order, и класс Product являются агрегатами, и фрагменты дают слишком четкое представление о том, что вы консолидируете «продукт Repository» из агрегата Order.

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

Помимо вышесказанного, я локально проверил, могу ли я использовать Integer в качестве совокупного идентификатора. Таким образом, как аннотированное поле @AggregateIdentifier в совокупности и как аннотированное поле @TargetAggregateIdentifier в вашем командном сообщении. Вдобавок ко всему, я попробовал это, заставив фреймворк вызвать операцию Repository#load(String) (таким образом, просто отправив команду на CommandGateway / CommandBus) и , вызвав ее напрямую, как вы это сделали.

К сожалению, у меня все хорошо. Таким образом, в качестве другого продолжения, я бы предложил поделиться фактическим исключением, которое вы получаете, когда выполняете операцию Repository#load(String).

...