我拥有DDD / CQRS应用程序。
我的问题涉及通过POST(Rest)处理项目创建。
CQRS(基于CQS原则)促进命令永远不会返回值 有查询。
所以我想知道如何处理Item创建的用例。
这是我当前的命令处理程序模式(示例的灯光(没有接口等)):
@Service
@Transactional
public CreateItem {
public void handle(CreateItemCommand command) {
Customer customer = customerRepository.findById(command.customerId);
ItemId generatedItemId = itemRepository.nextIdentity(); //generating the GUID
customer.createItem(generatedItemId, .....);
}
}
通过阅读this文章,一个简单的方法是在命令中声明一个输出属性,填充在handle
方法的末尾,如下所示:
public void handle(CreateItemCommand command) {
Customer customer = customerRepository.findById(command.customerId);
ItemId generatedItemId = itemRepository.nextIdentity(); //generating the GUID
customer.createItem(generatedItemId, .....);
command.itemId = generatedItemId; //populating the output property
}
但是,我发现这种方法有一个缺点: - 理论上,命令是不可变的。
这个itemId
将通过位置标头调用控制器(webapp)发送,状态为201或202(取决于我是否期望异步)。
另一种解决方案是让控制器通过访问存储库本身来初始化GUID,从而使命令不可变:
//in my controller:
ItemId generatedItemId = itemRepository.nextIdentity(); //controller generating the GUID
createItem.handle(command);
// setting here the location header (201-202) containing the URL to the newly created item with the using the previous itemId.
缺点:控制器(适配器层)直接访问存储库...,这是太低级别的IMO。
我的极端客户端是一个Javascript应用程序,我可能有另一个解决方案让Javascript本身生成一个GUID,并在将整个命令发送到服务器之前将CreateItemCommand
与它一起提供。
优势:不再存在可能违反CQ(R)S指南的问题 缺点:应检查服务器端传递的id的有效性。虽然会有一个唯一的索引,可以防止在数据库中意外插入。
处理此问题的最佳(或仅仅是好的)策略是什么?
答案 0 :(得分:2)
我是基于CQRS模式的CRM应用程序的开发人员。我倾向于认为命令是不可变的。该团队很早就决定,在客户端上生成所有ID以获得不可变命令。这是完全正常的,因为我们正在使用UUID。所以我们非常有信心,ID是有效的,没有ID冲突。到目前为止,我们采用了这种方法 - 我绝对可以推荐这个。在那种情况下,客户端只知道ID。
有时它会发生 - 特别是在手动测试中 - 使用相同的ID调度create命令两次。在这种情况下,事件存储中事件的添加失败(我们使用事件源),并且存在重复的键异常。异常传递给控制器。事实上,我们确实通过回调来返回命令执行的结果,即使它在大多数情况下只是“一切正常” - 所以没有抛出异常。命令验证也是这样完成的。我们使用命令总线概念来做到这一点。
我建议看看Axon框架。我们使用它,它提供了常见的构建块,它只是工作。也许你可以从中获得灵感!