假设我有一个集合,对于某些操作,它需要存在另一个集合。假设我有一个car
和一个garage
。可能有一个名为ParkInGarage
的命令,如下所示:
public class ParkInGarage {
@TargetAggregateIdentifier
public final UUID carId;
public final Garage garage;
//... constructor omitted
}
我已经read验证聚合的存在,在命令中使用加载的聚合是一种好习惯,因为这已经暗示了聚合的存在(而不是传递garageId
)。
现在,当使用Axon's fixtures对Car
进行单元测试时,我不能简单地说出Garage
来实例化new Garage(buildGarageCmd)
。它会说:
java.lang.IllegalStateException:如果没有作用域,则无法请求当前作用域
因为没有建立基础结构。 我将如何测试这种情况,还是应该以不同的方式设计聚合?
我正在使用的聚合根可能会引用自己以形成所述聚合根的树结构。我们称之为Node
。
@Aggregate
public class Node {
private Node parentNode;
}
创建后,我可以将Optional<Node>
作为父级传递,或者稍后使用单独的命令设置父级。问题的一部分是将父对象定义为实例还是按ID定义。
public class AttachNodeCmd {
@TargetAggregateIdentifier
public final UUID nodeId;
public final Optional<Node> parentNode;
}
在命令处理程序中,我需要检查将节点附加到给定的父级是否会引入一个循环(该结构应该是树,而不是普通图)。
@CommandHandler
public Node(AttachNodeCmd command) {
if (command.parentNode.isPresent()) {
Node currentNode = command.parentNode.get();
while (currentNode != null) {
if (currentNode.equals(this)) throw new RecursionException();
currentNode = currentNode.parentNode.orElse(null);
}
}
//Accept the command by applying() an Event
}
在某些时候,需要实例化父级以执行那些检查。这可以通过在命令中提供聚合实例(不推荐使用)来完成,也可以通过向命令处理程序提供Repository<Node>
和nodeId
来完成,而该命令处理程序本身就是聚合的并且也不鼓励使用。目前,我没有找到正确的方法来进行此操作,并且没有进一步测试的方法。
答案 0 :(得分:2)
我不会在命令中放入AR实例。命令模式应该是稳定的,并且因为它们是消息契约,所以应该易于序列化/重新序列化。
您可以做的是解决命令处理程序中的依赖项。
//ParkInGarage command handler
Garage garage = garageRepository.garageOfId(command.garageId);
Car car = carRepository.carOfId(command.carId);
car.parkIn(garage);
我一点都不了解Axon Framework,但是现在应该相对容易测试。
答案 1 :(得分:1)
我认为@plalx使您步入正轨。命令是您的API /消息合约的一部分,而在其中公开聚合并不是一个好主意。
另外,我想指出Axon中的AggregateFixtures
可以测试单个聚合,而不是聚合之间的操作协调。
通常在聚合/有界上下文之间进行协调时,会看到出现了sagas。现在说实话,我有点怀疑这个用例是否证明了Saga的合理性,但是我可以想象如果ParkCarInGarageCommand
因为Garage
聚合已满(例如)而失败,那么您需要通过另一条命令告诉Car
聚合是不行的。在Axon中设置的Saga可能会帮助您解决此问题,因为您可以轻松捕获(1)处理命令中的异常或(2)处理通知操作未成功的事件。