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