バックグラウンド
私は CQRS + ES を使用しているシステムを持っています。このシステムには、イベント ストアに永続化されるブログ投稿や問題などの集計があり、イベントをクエリ側に送信して、プロジェクションを介して読み取りモデルを永続化します。
問題や投稿が作成された場合、それはかなり簡単です
- クライアントは、新しい問題を作成するコマンドを作成します
- コマンド ハンドラーが新しい問題の集計を作成し、変更をイベント ストアに保存します。
- アグリゲートが変更を適用すると、IssueCreatedEvent などを発生させます。
- 読み取り側のプロジェクションはこれをリッスンし、課題モデルと必要なその他の非正規化データ (すべての課題のリストをクエリするためのカットダウン IssueListItem など) を作成します。
問題に変更が加えられ、 IssueStatusChangedなどの適切なイベントが書き込み側で発生し、それに応じて読み取り側で処理された場合。読み取り側で 2 つの非正規化モデルをロードし、イベントからステータスを更新して保存します。簡単。
コメントなどの関係はどのように扱っていますか?
ユーザーが問題やブログ投稿にコメントを投稿できるコメント システムを実装しています。私が最初に考えたのは、一貫性を保つために、これらのコメントを問題に追加するか、書き込み側の集計を投稿することでした。これについて考えてみると、誰かが問題を更新していて、他の誰かが来て新しいコメントを投稿したときのように、多くの不必要な並行性の問題が発生する可能性が高いことに気付きました。
これにより、コメント自体を独自の集約ルートとしてモデル化する必要があると考えるようになりました。このようにして、ブログ投稿または問題に投稿されたコメントが問題自体と競合することはありません。
したがって、書き込み側のコメントをこのように集計としてモデル化すると仮定すると、2 つの質問があります。
1) 書き込み側の問題または投稿集計は、この関係を保存する必要がありますか? コメント集約自体は、どのアイテムが投稿されたかを id 参照とともに既に保存しています。
もしそうなら、Issue Aggregate にコメント作成イベントをサブスクライブさせ、独自の参照を追加することを考えていました。
public class Issue : AggregateRoot, IEventHandler<CommentCreatedEvent>
{
private ICollection<Guid> _Comments;
public void Handle(CommentCreatedEvent @event)
{
_Comments.Add(@event.AggregateId)
}
}
コメントはすでにその親への参照を保存しているので、これで十分ですか、それとも必要ありませんか? このデータは、書き込み側では実際には必要ありませんが、すべてのコメントが読み込まれるのが親である場合、読み取り側ではより重要です。
2) 読み取り側では、このデータを保存する最良の方法は何ですか?
具体的には、このデータを簡単に更新できるようにするために、コメント用に別のテーブルを作成し、それらを適切な投稿または問題に結合する必要があります。コメントが終わったら、ユーザーがアイテムをフォローして更新を受け取ることができるフォロー システムを実装します。ただし、この道をたどると、すぐに読み取り側の高度に正規化されたスキーマに戻り、最適化された非正規化された読み取りモデルの目的が無効になります。
そのため、たとえば、すべてのコメントをシリアル化されたjson clobなどとして保存する単一の列を課題テーブルに追加することを考えていました。そうすれば、コメントに変更が加えられた場合でも、1 つのレコードを取り出して問題をロードし、コメントに適切な変更を加え (既存のコメントの更新、新しいコメントの追加、コメントの削除など)、レコードを再保存できます。 . 読み取りの観点からは、問題全体を一度に取得できます。
このアプローチの問題点は、たとえば、ユーザーがプロフィール写真やプロフィール名を変更した場合、すべての問題や投稿を読み込んで、コメントを読み込んで、コメント情報に適切な変更を加えなければならないことです。
また、文書データベース (読み取り側で検討している別のもの) が、ネストされたデータの更新に関するこの問題をどのように回避するのだろうか?