免责声明:我对事件溯源、Axon 框架和 DDD 完全陌生,所以我做错了什么的可能性很大。
我正在做一个应用程序来管理事件 - 会议、研讨会等。
我有一个聚合根,即 EventProposal。应该有可能将 TodoList 分配给给定的 EventProposal。TodoList 是另一个聚合根,由 TodoItems - 实体组成。TodoItem 可以编辑,标记为完成等。
需要将 TodoItem 分配给 TodoList,我是这样实现的:
public class TodoList extends AbstractAnnotatedAggregateRoot {
@AggregateIdentifier
private TodoListId id;
@EventSourcedMember
private List<TodoItem> todoItems = Lists.newArrayList();
...
public void assignTodoItem(TodoItemId todoItemId, String content, LocalDate creationDate) {
if (alreadyHasTodoItemWith(todoItemId)) {
apply(new TodoItemNotAssignedToTodoList(
id, todoItemId)
);
return;
}
apply(new TodoItemAssignedToTodoListEvent(
id, todoItemId, content, creationDate)
);
}
@EventSourcingHandler
public void on(TodoItemAssignedToTodoListEvent event) {
final TodoItem item = TodoItemFactory.create(
event.todoItemId(),
event.description(),
event.createdAt()
);
todoItems.add(item);
}
对应的成功路径命令和事件:
@Value
@Accessors(fluent = true)
public class AssignTodoItemToTodoListCommand {
@TargetAggregateIdentifier
private final TodoListId todoListId;
private final TodoItemId todoItemId;
private final String description;
private final LocalDate createdAt;
}
@Value
@Accessors(fluent = true)
public class TodoItemAssignedToTodoListEvent {
private final TodoListId todoListId;
private final TodoItemId todoItemId;
private final String description;
private final LocalDate createdAt;
}
这可以通过 Axon 的 BDD 方式轻松测试。(GivenThenFixture)
但是现在 - 还有另一个要求:应该能够在现有的 TodoListTemplate 上创建 TodoList。模板只是包装了 TodoItemTemplates 集合的聚合。
我的实现问题出现了。我尝试了类似的东西(在 TodoList 类中):
public void fulfillWith(TodoListTemplate todoListTemplate, LocalDate creationDate) {
if (alreadyHasAnyTodoItem()) {
apply(new TodoListNotFulfilledWithTemplateEvent(
id,
todoListTemplate.id()
)
);
return;
}
apply(new TodoListFulfilledWithTemplateEvent(
id,
todoListTemplate.id(),
todoListTemplate.todoItemDescriptions(),
creationDate
)
);
}
@EventSourcingHandler
public void on(TodoListFulfilledWithTemplateEvent event) {
todoItems.addAll(
fromDescriptions(event.todoItemDescriptions(), event.fulfilledAt())
);
}
private Collection<TodoItem> fromDescriptions(Collection<String> descriptions, LocalDate creationDate) {
return descriptions.stream()
.map(description -> TodoItemFactory.create(description, creationDate))
.collect(Collectors.toList());
}
同样,命令和事件:
@Value
@Accessors(fluent = true)
public class FulfillTodoListWithTemplateCommand {
private final TodoListId todoListId;
private final TodoListTemplateId todoListTemplateId;
private final LocalDate creationDate;
}
@Value
@Accessors(fluent = true)
public class TodoListFulfilledWithTemplateEvent {
private final TodoListId todoListId;
private final TodoListTemplateId todoListTemplateId;
private final List<String> todoItemDescriptions;
private final LocalDate fulfilledAt;
}
问题:如您所见 -TodoItemFactory
涉及类,生成唯一 ID:
public static TodoItem create(String content, LocalDate createdAt) {
return TodoItemFactory.create(nextId(), content, createdAt);
}
这样它就不能用轴突测试——它给了我错误org.axonframework.test.AxonAssertionError: Illegal state change detected!
这很明显——工作聚合的 ID 与应用事件后构造的 ID 不同。
最后,我的问题来了:
我应该如何解决这个问题?
- 在某处生成这些 ID 并将它们包含在输出的 TodoListFulfilledWithTemplateEvent 中?这导致在一个事件中有两个集合 - 一个用于 ID,一个用于项目内容/描述。
- 假设我得到了之前生成的那些 ID,并且不仅将它们包含在输出事件中,而且还包含在传入命令中。这导致与以前相同的丑陋,但两次。
- 以我多次调用“assingTodoItemToTodoList”方法的方式执行此操作,这将生成大量事件并可能产生重新排序,因为事件是异步的。
抱歉冗长,我试图尽可能具体。