如何实例化聚合以测试其他聚合?
How are aggregates instantiated to test other aggregates with?
假设我有一个聚合,对于某些操作,需要存在另一个聚合。假设我有一个 car
和一个 garage
。可能有一个名为 ParkInGarage
的命令,看起来像这样:
public class ParkInGarage {
@TargetAggregateIdentifier
public final UUID carId;
public final Garage garage;
//... constructor omitted
}
我已经 read 验证聚合的存在,最好在命令中使用加载的聚合,因为这已经暗示了它的存在(而不是传递 garageId
).
现在,当使用 Axon's fixtures 对 Car
进行单元测试时,我不能简单地通过说 new Garage(buildGarageCmd)
来实例化我的 Garage
。它会说:
java.lang.IllegalStateException: Cannot request current Scope if none is active
因为没有设置基础设施。
我将如何测试这种情况,或者我应该以不同的方式设计聚合?
抽象的真实示例
我正在使用的聚合根可能具有对自身的引用以形成所述聚合根的树结构。我们称它为 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
来完成,命令处理程序是聚合本身,也不鼓励。目前我没有看到正确的方法来执行此操作并且进一步测试它的方法。
我不会将 AR 实例放入命令中。命令架构应该稳定且易于 serialize/reserialize 因为它们是消息协定。
您可以改为解决命令处理程序中的依赖关系。
//ParkInGarage command handler
Garage garage = garageRepository.garageOfId(command.garageId);
Car car = carRepository.carOfId(command.carId);
car.parkIn(garage);
我完全不知道 Axon Framework,但现在应该比较容易测试。
我认为@plalx 让您走上了正确的轨道。命令是您的 API/Message 合同的一部分,在其中公开聚合并不是一个好主意。
此外,我想指出 Axon 中的 AggregateFixtures
用于测试单个聚合,而不是聚合之间的操作协调。
aggregates/bounded 上下文之间的协调通常是您看到 sagas 发挥作用的地方。老实说,我有点怀疑这个用例是否证明了 Saga 的合理性,但我可以想象如果 ParkCarInGarageCommand
失败是因为 Garage
聚合已满(例如),那么你需要通过另一个命令指示 Car
聚合,告诉它这是不行的。在 Axon 中设置的 Saga 可能会帮助您解决这个问题,因为您可以轻松捕获 (1) 处理命令的异常或 (2) 处理通知操作不成功的事件。
假设我有一个聚合,对于某些操作,需要存在另一个聚合。假设我有一个 car
和一个 garage
。可能有一个名为 ParkInGarage
的命令,看起来像这样:
public class ParkInGarage {
@TargetAggregateIdentifier
public final UUID carId;
public final Garage garage;
//... constructor omitted
}
我已经 read 验证聚合的存在,最好在命令中使用加载的聚合,因为这已经暗示了它的存在(而不是传递 garageId
).
现在,当使用 Axon's fixtures 对 Car
进行单元测试时,我不能简单地通过说 new Garage(buildGarageCmd)
来实例化我的 Garage
。它会说:
java.lang.IllegalStateException: Cannot request current Scope if none is active
因为没有设置基础设施。 我将如何测试这种情况,或者我应该以不同的方式设计聚合?
抽象的真实示例
我正在使用的聚合根可能具有对自身的引用以形成所述聚合根的树结构。我们称它为 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
来完成,命令处理程序是聚合本身,也不鼓励。目前我没有看到正确的方法来执行此操作并且进一步测试它的方法。
我不会将 AR 实例放入命令中。命令架构应该稳定且易于 serialize/reserialize 因为它们是消息协定。
您可以改为解决命令处理程序中的依赖关系。
//ParkInGarage command handler
Garage garage = garageRepository.garageOfId(command.garageId);
Car car = carRepository.carOfId(command.carId);
car.parkIn(garage);
我完全不知道 Axon Framework,但现在应该比较容易测试。
我认为@plalx 让您走上了正确的轨道。命令是您的 API/Message 合同的一部分,在其中公开聚合并不是一个好主意。
此外,我想指出 Axon 中的 AggregateFixtures
用于测试单个聚合,而不是聚合之间的操作协调。
aggregates/bounded 上下文之间的协调通常是您看到 sagas 发挥作用的地方。老实说,我有点怀疑这个用例是否证明了 Saga 的合理性,但我可以想象如果 ParkCarInGarageCommand
失败是因为 Garage
聚合已满(例如),那么你需要通过另一个命令指示 Car
聚合,告诉它这是不行的。在 Axon 中设置的 Saga 可能会帮助您解决这个问题,因为您可以轻松捕获 (1) 处理命令的异常或 (2) 处理通知操作不成功的事件。