第25章:スナップショット概念(なぜ必要?)📸⚡
この章でできるようになること 🎯✨
- スナップショットが 何のためにあるのか を説明できる📸
- 「いつ入れるべきか/まだ要らないか」を 判断できる ✅
- スナップショットの 取り方のパターン(N件ごと・時間ごと等)を選べる🧠
- 次章(最小実装)に向けて、設計メモを作れる📝
1) そもそもスナップショットって何?📸

イベントソーシングでは、集約(Aggregate)の現在状態は イベント列を最初から順に Apply して復元(Rehydrate) して作りますよね🔁
でも、イベントが増えると…
- 復元のたびに 読むイベント数が増える 📚
- Apply回数が増えて CPU時間も増える 🧠💦
そこで登場するのが スナップショット(Snapshot)です✨ スナップショットは「あるバージョン(version)時点の 集約の状態を丸ごと保存 したもの」です📦 復元するときは「最新スナップショット + そこから後のイベントだけ」を当てればOKになります✅
重要:スナップショットは “設計の主役” じゃなくて 性能最適化の道具 です📌 まずはシンプルに作って、必要になったら入れる、が基本! (martendb.io)
2) スナップショットが欲しくなる「あるある」😵💫
次のどれかが起きると、スナップショットを検討する価値が出ます👇
A. 1つのストリームにイベントが多すぎる📚📚📚
たとえば「ユーザー設定」「買い物カート」「サブスク契約」みたいに、長期で更新され続ける集約はイベントが積もりがち。
B. コマンド処理が遅くなってきた🐢💦
コマンド処理はだいたい Load(復元)→ Decide(ルール判定)→ Append(保存) なので、Load が遅いと全体が遅くなります⏱️
C. “高い頻度で復元される” 集約がある🔁
同じ集約が短時間に何度も更新されると、復元コストが効いてきます💥
3) まず覚える「大原則」3つ 🧷✅
原則①:スナップショットは“最初から入れない”🙅♀️
スナップショットは便利だけど、書き込みが増えるし、運用が複雑になります🧯 だから「必要になるまで入れない」が推奨されがちです。 (martendb.io)
原則②:毎回スナップショットを取らない🙅♀️📸
イベントを1つ書くたびにスナップショットも書くと、 「速くするための機能」が「遅くする原因」になりがち…😇 一般には 間隔(一定イベント数ごと、または特定イベント発生時など)で取ります。 (martendb.io)
原則③:スナップショットの前に“構造”を疑う🧠🔍
イベントが増えすぎる原因が「集約がデカすぎ」なら、 スナップショットより先に 集約の境界見直し で解決できることもあります⚖️ (EventSourcingDB)
4) どのタイミングで取る?代表パターン4つ 📈📸
パターンA:N件ごと(例:100件ごと)🔢
- いちばん分かりやすい王道✨
- 「スナップショットを読む + 最大N件のイベントだけ再生」で復元できる
- Nは、集約の重さ次第(数百ごと、などの経験談もあります) (Kurrent - event-native data platform)
向いてる:イベント数が素直に増え続ける集約 弱点:Nの根拠が薄いと、調整が迷子になる🌀
パターンB:時間ごと(例:1日1回、1時間1回)🕒
- “増え方” が一定ではない場合に便利
- バッチ的に取れる(夜間に取るなど)🌙
向いてる:日次・週次の更新が多い、運用都合がある 弱点:更新が集中する時間帯だと、その時間の復元が重いことも💦
パターンC:特定イベントが来たら取る(節目)🏁
例:
OrderPlacedの直後だけSubscriptionRenewedの直後だけ
「この節目の状態が重要」「ここからの復元が重い」みたいなときに効きます✨ Martenのドキュメントでも「一定間隔 or 特定イベントタイプで」がよくある、と説明されています。 (martendb.io)
パターンD:混合(N件ごと + 節目)🧩
- 通常はN件ごと
- でも “特別に重いイベント” が来たら即スナップショット
向いてる:イベントの種類で重さがバラバラな集約
5) スナップショットに入れるもの(最低限)🍱🏷️
スナップショットは「状態の保存」なので、最低限これが必要です👇
AggregateId(どの集約?)Version(どのイベントまで反映済み?)🔢State(集約の状態本体)📦CreatedAt(いつ作った?)🕒(あると便利)
コツ:スナップショットの中身は「復元に必要な状態だけ」に絞ると安全です✅ “画面表示用の情報” まで詰めると、変更に弱くなりがち😵💫
さらに設計としては「スナップショットも専用タイプとして扱う(イベント扱いに近い形)」みたいな方針もあります。 (EventSourcingDB)
6) よくある事故パターン(ここ超大事)🧯😱
事故①:Versionズレ(これ一番多い)💥
スナップショットが Version=120 なのに、
後続イベントを 121 から適用しないといけないのに間違えて 120 から適用…
→ 二重適用で壊れます😇
✅ 対策:
- 「スナップショットは versionまで適用済み」を厳密に定義
- 復元コードで
snapshot.Version + 1から読むように固定
事故②:スナップショットが壊れてる(でもイベントは正しい)🧨
スナップショットは “最適化データ” なので、壊れると厄介💦
✅ 対策:
- 「スナップショットが読めなければ、イベント最初から復元する」フォールバックを用意
- スナップショットは再生成できる設計にする🔁
- スナップショットは再生成できる設計にする🔁
事故③:書き込み増えすぎ(速くするはずが遅い)📸📸📸
スナップショット頻度が高いと、IOが増えて逆効果になりがち。 「毎回は取らず、間隔で」が基本です。 (martendb.io)
事故④:保存場所の設計が微妙で運用地獄🌀
例として EventStoreDB まわりでは「スナップショット用に別ストリームを作り、最大1件だけ保持する」みたいなやり方が紹介されることがあります(最新だけ残す)。 (ITNext) (※あなたの自作EventStoreでも、同じ考え方でテーブルやキー設計ができます👌)
7) “入れるかどうか”判断チェックリスト ✅📝
次の質問にYESが増えたら、スナップショット検討です📸
- 1ストリームのイベント数が 数百〜数千 になってきた
- コマンド処理の遅さが 体感できる(or 計測で出た)⏱️
- 同じ集約を短時間に何度も復元している🔁
- でも集約分割(境界見直し)では解決しにくい⚖️
- スナップショット導入の 追加書き込み を許容できる✍️
「計測してから入れる」がいちばん強いです💪 .NET 10 でも診断・観測が強化されていて、計測の重要性はますます上がってます。 (Microsoft Learn)
8) ミニ演習:イベント100件の“重さ”を体感しよう 😵💫➡️😲
この章は概念回なので、実装は次章でやります! でも「遅さの正体」を体感すると理解が爆上がりします💡
やること(手順)🧪
- すでに作った集約に、イベントを 100件 連続でAppend(テストでOK)
Rehydrateを 10回 ループしてStopwatchで時間を計る- ついでに
Applyの中で「ちょい重い処理」を わざと追加(例:簡単なループ) - 「イベント数が増えるほど復元が重くなる」を体感する
計測の最小コード例(雰囲気)⏱️
using System.Diagnostics;
var sw = Stopwatch.StartNew();
for (int i = 0; i < 10; i++)
{
var agg = await repository.LoadAsync(id); // ここがイベント再生(復元)
}
sw.Stop();
Console.WriteLine($"rehydrate x10: {sw.ElapsedMilliseconds}ms");
✅ 観察ポイント👀
- イベント数が増えると、復元が比例して重くなる
- Applyが重いと、さらに効く
- だから「最新状態を途中保存できたら嬉しい」ってなる📸✨
9) AI活用(Copilot / Codex)プロンプト例 🤖💬✨
目的:スナップショット導入の“設計メモ”を作る📝
- 「N件ごと」「時間ごと」「節目イベント」の候補を3つ出して
- それぞれのメリデメと、採用条件(こうなったら入れる)を書いて
- うちの題材(カート/ToDo等)に合わせて例もつけて
目的:次章の実装に向けた“雛形”を作る🧱
Snapshotレコード(AggregateId, Version, State, CreatedAt)を定義してLoadで「スナップショット読んで、残りイベントだけ読む」流れを疑似コードで
AIに出してもらったら、必ずチェックするのはここ👇
- versionの扱い(+1の読み開始)
- “毎回スナップショット保存” になってないか
- Stateに詰めすぎてないか(表示用まで入れてない?)
まとめ 🎀
- スナップショットは 復元(Rehydrate)を速くするための最適化 📸⚡ (martendb.io)
- まずは入れずに作って、必要になったら入れるのが基本✅ (martendb.io)
- 取り方は「N件ごと」「時間ごと」「節目イベント」が代表🧠 (Kurrent - event-native data platform)
- 最大の事故は versionズレ!ここは設計でガチガチに固定する🔒
次章チラ見せ 👀✨
次の第26章では、いま学んだ概念を使って
「最新スナップショット + 残りイベント」 で復元する最小実装を作ります📸🧪