免責事項: 私はイベント ソーシング、Axon フレームワーク、および DDD にまったく慣れていないため、何か間違ったことをしている可能性が非常に高いです。
会議、ワークショップなどのイベントを管理するためのアプリケーションを実行しています。
EventProposal という集約ルートが 1 つあります。特定の EventProposal に TodoList を割り当てる可能性があるはずです。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);
}
対応する success-path コマンドとイベント:
@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 を作成できる必要があります。Template は、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 をどこかで生成し、今後の TodoListFulfilledWithTemplateEvent に含めますか? これにより、1 つのイベントに 2 つのコレクションが含まれることになります。1 つは ID 用、もう 1 つはアイテムのコンテンツ/説明用です。
- これらの ID を以前に生成し、それらを今後のイベントだけでなく、着信コマンドにも含めるとします。これは前と同じ醜さにつながりますが、2 倍になります。
- 「assingTodoItemToTodoList」メソッドを複数回呼び出す方法で実行します。これにより、多くのイベントが生成され、イベントが非同期であるため、並べ替えが発生する可能性があります。
冗長で申し訳ありませんが、できるだけ具体的にしようとしました。