axon:在创建聚合之后,在saga中创建并保存另一个聚合

gijlo24d  于 2021-07-03  发布在  Java
关注(0)|答案(1)|浏览(484)

更新:问题似乎是我使用了两次的id,或者换句话说,是我想要用于productinventory实体的产品实体的id。一旦我为productinventory实体生成了一个新的id,它似乎就可以正常工作了。但我希望两者的id相同,因为它们是同一个产品。
我有两项服务:
productmanagementservice(使用产品详细信息保存产品实体)
1.)为了保存产品实体,我实现了一个eventhandler,它侦听productcreatedevent并将产品保存到mysql数据库。
productinventoryservice(将具有产品库存量的productinventory实体保存到productmanagementservice中定义的某个productid)
2.)为了保存productinventory实体,我还实现了一个eventhandler,它侦听productinventorycreatedevent并将产品保存到mysql数据库。
我想做的是:
在productmanagementservice中创建新产品时,我希望在productinventoryservice中直接创建productinventory实体,并将其保存到我的msql表中。新产品库存实体应具有与产品实体相同的id。
为了实现这一点,我创建了一个saga,该saga将列出productcreatedevent并发送一个新的createproductinventorycommand。一旦createproductinventorycommand触发productinventorycreatedevent,eventhandler(如2.)中所述)就会捕获它。但事实并非如此。
thta唯一保存的是产品实体,因此总结起来:
1.)有效,2.)无效。会创建productinventory聚合,但不会保存它,因为不会触发连接到eventhandler的保存进程。
我也遇到了一个例外,应用程序没有崩溃: Command 'com.myApplication.apicore.command.CreateProductInventoryCommand' resulted in org.axonframework.commandhandling.CommandExecutionException(OUT_OF_RANGE: [AXONIQ-2000] Invalid sequence number 0 for aggregate 3cd71e21-3720-403b-9182-130d61760117, expected 1) 我的传奇:

@Saga
@ProcessingGroup("ProductCreationSaga")
public class ProductCreationSaga {

    @Autowired
    private transient CommandGateway commandGateway;

    @StartSaga
    @SagaEventHandler(associationProperty = "productId")
    public void handle(ProductCreatedEvent event) {
        System.out.println("ProductCreationSaga, SagaEventHandler, ProductCreatedEvent");

        String productInventoryId = event.productId;
        SagaLifecycle.associateWith("productInventoryId", productInventoryId);
    //takes ID from product entity and sets all 3 stock attributes to zero
        commandGateway.send(new CreateProductInventoryCommand(productInventoryId, 0, 0, 0));
    }

    @SagaEventHandler(associationProperty = "productInventoryId")
    public void handle(ProductInventoryCreatedEvent event) {
        System.out.println("ProductCreationSaga, SagaEventHandler, ProductInventoryCreatedEvent");

        SagaLifecycle.end();
    }

}

按预期工作并保存产品实体的eventhandler:

@Component
public class ProductPersistenceService {

    @Autowired
    private ProductEntityRepository productRepository;

    //works as intended
    @EventHandler
    void on(ProductCreatedEvent event) {
        System.out.println("ProductPersistenceService, EventHandler, ProductCreatedEvent");
        ProductEntity entity = new ProductEntity(event.productId, event.productName, event.productDescription, event.productPrice);
        productRepository.save(entity);

    }

    @EventHandler
    void on(ProductNameChangedEvent event) {
        System.out.println("ProductPersistenceService, EventHandler, ProductNameChangedEvent");
        ProductEntity existingEntity = productRepository.findById(event.productId).get();

        ProductEntity entity = new ProductEntity(event.productId, event.productName, existingEntity.getProductDescription(), existingEntity.getProductPrice());
        productRepository.save(entity);

    }
}

应保存productinventory实体但不保存的eventhandler:

@Component
public class ProductInventoryPersistenceService {

    @Autowired
    private ProductInventoryEntityRepository productInventoryRepository;

    //doesn't work
    @EventHandler
    void on(ProductInventoryCreatedEvent event) {
        System.out.println("ProductInventoryPersistenceService, EventHandler, ProductInventoryCreatedEvent");
        ProductInventoryEntity entity = new ProductInventoryEntity(event.productInventoryId, event.physicalStock, event.reservedStock, event.availableStock);
        System.out.println(entity.toString());

        productInventoryRepository.save(entity);

    }

}

产品聚合:

@Aggregate
public class Product {

    @AggregateIdentifier
    private String productId;

    private String productName;

    private String productDescription;

    private double productPrice;

    public Product() {
    }

    @CommandHandler
    public Product(CreateProductCommand command) {
        System.out.println("Product, CommandHandler, CreateProductCommand");
        AggregateLifecycle.apply(new ProductCreatedEvent(command.productId, command.productName, command.productDescription, command.productPrice));
    }

    @EventSourcingHandler
    protected void on(ProductCreatedEvent event) {
        System.out.println("Product, EventSourcingHandler, ProductCreatedEvent");

        this.productId = event.productId;
        this.productName = event.productName;
        this.productDescription = event.productDescription;
        this.productPrice = event.productPrice;
    }

}

产品库存合计:

@Aggregate
public class ProductInventory {

    @AggregateIdentifier
    private String productInventoryId;

    private int physicalStock;

    private int reservedStock;

    private int availableStock;

    public ProductInventory() {
    }

    @CommandHandler
    public ProductInventory(CreateProductInventoryCommand command) {
        System.out.println("ProductInventory, CommandHandler, CreateProductInventoryCommand");
        AggregateLifecycle.apply(new ProductInventoryCreatedEvent(command.productInventoryId, command.physicalStock, command.reservedStock, command.availableStock));
    }

    @EventSourcingHandler
    protected void on(ProductInventoryCreatedEvent event) {
        System.out.println("ProductInventory, EventSourcingHandler, ProductInventoryCreatedEvent");

        this.productInventoryId = event.productInventoryId;
        this.physicalStock = event.physicalStock;
        this.reservedStock = event.reservedStock;
        this.availableStock = event.availableStock;
    }

}
os8fio9y

os8fio9y1#

您现在注意到的是给定事件存储中[aggregate identifier,sequence number]对的唯一性要求。这个要求是为了保护您不受对同一聚合示例的潜在并发访问,因为同一聚合的多个事件都需要具有唯一的总序列号。此外,此数字还用于标识需要处理的事件的顺序,以确保以相同的顺序一致地重新创建聚合。
因此,您可能会认为这会选择“抱歉,没有合适的解决方案”,但幸运的是事实并非如此。在这个设置中,大致可以做三件事:
事实上,两个集合都有唯一的标识符。
在两个应用程序之间使用不同的有界上下文。
更改写入聚合标识符的方式。
选择1可以说是最务实的,也是大多数人使用的。不过,您已经注意到重用标识符是必要的,所以我假设您已经完全忽略了这一选项。不管怎样,我会尝试重新使用这种方法 UUID 对于您创建的每个新实体,每个默认值都可以避免将来的麻烦。
选项2将通过ddd引入的有界上下文概念来反映自身。让 Product 骨料和 ProductInventory 聚合驻留在不同的上下文中意味着您将拥有两个不同的事件存储。因此,将保留唯一性约束,因为没有一个存储同时包含两个聚合事件流。然而,这种方法是否可行取决于两个聚合是否真正属于同一上下文是/否。如果是这样,您可以使用axon服务器的多上下文支持来创建两个不同的应用程序。
选项3需要了解轴突的作用。当它存储事件时,它将调用 toString() 上的方法 @AggregateIdentifier 聚合中带注解的字段。作为你的 @AggregateIdentifier 带注解的字段是 String ,将按原样为您提供标识符。您可以做的是输入标识符,对于 toString() 方法不仅返回标识符,还将聚合类型附加到它。这样做将使 aggregateIdentifier 独特的,而从使用的Angular 来看,似乎仍然是重用标识符。
从我的Angular 很难推断这三个选项中哪一个更适合你的解决方案。我所做的,是从我的Angular ,以最合理的方式排列它们。希望这能帮助你进一步@jan!

相关问题