Java Axon:创建聚合后,在Saga中创建并保存另一个聚合
更新:问题似乎是我使用了两次的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,它将列出ProductCreatedEvent并发送一个新的CreateProductInventoryCommand。一旦CreateProductInventoryCommand触发ProductInventoryCreatedEvent,EventHandler(如2.)中所述)就会捕获它。但事实并非如此 thta保存的唯一内容是产品实体,因此,总而言之: 1.)有效,2.)无效。确实会创建ProductInventory聚合,但不会保存,因为未触发连接到EventHandler的保存过程 我还遇到一个异常,但应用程序没有崩溃:Java Axon:创建聚合后,在Saga中创建并保存另一个聚合,java,spring-boot,event-handling,event-sourcing,axon,Java,Spring Boot,Event Handling,Event Sourcing,Axon,更新:问题似乎是我使用了两次的id,或者换句话说,是我想要用于productinventory实体的产品实体的id。一旦我为productinventory实体生成了一个新id,它似乎就可以正常工作了。但我想两者都有相同的id,因为它们是相同的产品 我有两项服务: ProductManagementService(使用产品详细信息保存产品实体) 1.)为了保存产品实体,我实现了一个EventHandler,它侦听ProductCreatedEvent并将产品保存到mysql数据库 Product
Command'com.myApplication.apicore.Command.CreateProductInventoryCommand'导致org.axonframework.commandhandling.CommandExecutionException(超出范围:[AXONIQ-2000]聚合3cd71e21-3720-403b-9182-130d61760117的序列号0无效,应为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;
}
}
产品库存合计:
@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;
}
}
您现在注意到的是给定事件存储中[aggregate identifier,sequence number]对的唯一性要求。此要求是为了防止对同一聚合实例的潜在并发访问,因为同一聚合的多个事件都需要具有唯一的总序列号。此外,此数字还用于标识事件需要处理的顺序,以确保以相同的顺序一致地重新创建聚合 因此,您可能认为这会选择“抱歉,没有解决方案”,但幸运的是,情况并非如此。在此设置中,您可以大致执行三项操作:
UUID
s可以避免您将来遇到麻烦
选项2将通过引入的有界上下文概念反映自身。让产品
聚合和ProductInventory
聚合驻留在不同的上下文中,这意味着您将为两者提供不同的事件存储。因此,将保留唯一性约束,因为没有单个存储同时包含两个聚合事件流。然而,这种方法是否可行取决于两个聚合是否真正属于相同的上下文是/否。如果是这种情况,您可以使用Axon服务器创建两个不同的应用程序
选项3需要了解Axon的功能。当它存储事件时,它将调用聚合中带注释的@AggregateIdentifier
字段上的toString()
方法。由于@AggregateIdentifier
注释字段是字符串
,因此将按原样为您提供标识符。您可以做的是使用类型化标识符,toString()
方法不会只返回标识符,而是将聚合类型附加到它。这样做将使存储的aggregateIdentifier
唯一,而从使用角度看,您似乎仍然在重用标识符
从我的角度来看,很难推断这三个选项中哪一个更适合您的解决方案。从我的角度来看,我所做的是以最合理的方式排列它们。
希望这将有助于你的进一步@Jan 非常感谢您的详细和有益的回答!