第10章:整合性の期待は1種類じゃない(やさしめ)🌈📘
この章でわかること🎯✨
- 「整合性=強い/弱い」の二択じゃなくて、どの“期待”を守るかで考える感覚🧠✨
- 特に大事な「ユーザー単位の整合性(自分の操作がちゃんと見える!)」の作り方👀✅
- CampusCafe(学食モバイルオーダー)を例に、画面ごとに必要な整合性を決める練習☕📱
10.1 「整合性」って、誰にとっての“正しさ”?🤔💭
同じデータでも、見る人・見るタイミングで「正しさ」の期待が変わるよ〜!
たとえば CampusCafe で…
- あなたが注文した直後に「注文履歴」を見た → “さっきの注文、見えないんだけど!?😱” はダメ
- 別の人が「人気メニューランキング」を見た → 数分ズレてても、たいてい “まあいいか😊” で済むことが多い
つまり、整合性は 画面や体験(UX)ごとに必要度が違うんだよね🌸✨
10.2 整合性の“期待”はグラデーション🎨🧩
ざっくり3段階で捉えるとわかりやすいよ👇
- 全員で同じに見える(強め) 🧱
- “自分に関しては”ちゃんと揃う(ユーザー単位) 👤✅
- だいたい揃えばOK(弱め) 🕰️
この章は②の「ユーザー単位」が主役!🎉
10.3 「ユーザー単位の整合性」って何?👤✨
ポイントはこれ👇
自分がやった操作は、次の自分の閲覧で必ず見える (“自分だけは裏切らない”整合性💗)
分散DBやクラウドDBでも、この発想はすごく大事。 たとえば Azure Cosmos DB の Session consistency は、同一セッション内で「read-your-writes(自分の書き込みが読める)」や「monotonic reads(読んだのが古く戻らない)」などを保証する、って説明されてるよ📘✨ (Microsoft Learn)
10.4 覚えておくと強い!4つの“セッション保証”🧠🔑
難しい理論より、「ユーザー体験で困るやつ」を防ぐ道具だと思ってOK😊✨
① Read-your-writes(自分の書いたものが見える)✍️➡️👀
- 例:注文ボタン押した直後、注文履歴に出ないのはイヤ😵💫
- 守りたいUX:「操作したのに反映されない」不安を消す💕
② Monotonic reads(読んだものが“古く戻らない”)📈🔁
- 例:注文履歴で 「注文あり」→ 次の更新で「注文なし」に戻る(え?消えた!?)はイヤ😱
③ Monotonic writes(同じ人の書き込み順が崩れない)🧾➡️🧾
- 例:同じ人が「注文→キャンセル」したのに、処理順が逆になって地獄💥
④ Writes-follow-reads(読んだ内容より古い状態に書かない)📖➡️✍️
- 例:在庫を見て「残り1」と思って注文したのに、古い状態に基づいた処理が混ざる…など😵
この4つは “セッション(ユーザー単位)” の整合性でよく出てくる考え方だよ📘✨ (ウィキペディア)
10.5 CampusCafe:画面ごとに「守りたい整合性」を決めよう🖥️✅
同じアプリでも画面ごとに違うよ〜!🌈
① 注文完了画面(注文直後)🧾✨
- 必須:Read-your-writes ✅ 「注文できた」って出たなら、履歴にも出てほしい!
② 注文履歴(自分の履歴)📜👤
- ほぼ必須:Read-your-writes / Monotonic reads ✅✅ 見えたり消えたりは不安すぎる…😢
③ 在庫表示(メニュー一覧の残数)📦🍞
-
ケース分けがコツ:
- “表示”:少し古くてもOKなことが多い(弱め)🕰️
- “確保”:ズレると困る(強め or 工夫必須)🧱⚠️ 「表示」と「確保」を同じ整合性で考えると、だいたい事故るよ〜💥
④ みんなのランキング(人気メニュー)🏆🍔
- だいたいでOK:最終的に合えば良い(弱め)😊
⑤ キッチン側ダッシュボード(調理の順番)👩🍳📋
- 必須になりがち:Monotonic writes / 順序 ✅ 順番が入れ替わると現場が混乱する〜😵💫
10.6 実装の“考え方”:全部を強整合にしないで勝つ💡🏁
整合性を強くすると、だいたい 遅くなる / 止まりやすくなる 方向になりがち。 だからコツは👇
- ユーザー体験に直撃する画面だけ「ユーザー単位の整合性」を厚くする👤✅
- それ以外は 最終的整合性で軽くする🕰️✨
このバランス感覚が CAP の“肌感覚”に直結するよ🌍⚖️
ちなみに実在DBでも、読み取りは「強い読み/結果整合の読み」みたいに選べるものがある(例:DynamoDB の strongly consistent / eventually consistent reads)📘 (AWS ドキュメント) 逆に「めっちゃ強い整合性」を提供するDBもあって、Spanner はトランザクション整合性(external consistency / strict serializability)を説明してるよ🧠✨ (Google Cloud Documentation)
10.7 ミニ演習:CampusCafeの“整合性ラベル”を貼ろう🏷️✨
次の表を埋めてみてね!📝💕
| 画面/機能 | 期待する整合性(強/ユーザー単位/弱) | 欲しい保証(例:RYW/Monotonic) | 理由(ユーザーの気持ちで) |
|---|---|---|---|
| 注文完了 | |||
| 注文履歴(自分) | |||
| メニュー在庫表示 | |||
| 在庫確保(注文処理内) | |||
| 人気ランキング | |||
| 調理キュー(キッチン) |
💡書き方の例(1行だけ見本)
- 注文履歴(自分)=ユーザー単位/RYW + Monotonic reads/「反映されないと不安で連打しちゃう😢」
10.8 ちいさなC#デモ:最終的整合っぽい“ズレ”を作ってみる🧪🐢
ここでは「レプリカ反映が遅れる」っぽい状況を わざと作るよ! (本物の分散DBじゃなくても、挙動のイメージが掴めればOK😊)
Primary:書き込み先(最新)Replica:読み取り先(反映が遅れる)
そして「ユーザー単位の整合性」を作るために、“自分の最新バージョン”をヘッダで渡す作戦にするよ🔑✨ (Cosmos DB の session token みたいな発想だと思ってOK)(Microsoft Learn)
using System.Collections.Concurrent;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var primary = new ConcurrentDictionary<Guid, OrderRecord>();
var replica = new ConcurrentDictionary<Guid, OrderRecord>();
// ユーザーごとの「自分が最後に書いたバージョン」(超簡易)
var userLatest = new ConcurrentDictionary<string, long>();
app.MapPost("/orders", async (PlaceOrderRequest req) =>
{
var orderId = Guid.NewGuid();
var now = DateTimeOffset.UtcNow;
// バージョンは適当な連番(本物はDBのLSNとかでもOK)
var version = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var record = new OrderRecord(orderId, req.UserId, req.MenuId, "Placed", now, version);
primary[orderId] = record;
userLatest[req.UserId] = Math.Max(userLatest.GetValueOrDefault(req.UserId), version);
// レプリカ反映を遅らせる(= 最終的整合性の“ズレ”を再現🐢)
_ = Task.Run(async () =>
{
await Task.Delay(1500);
replica[orderId] = record;
});
return Results.Ok(new
{
orderId,
sessionVersion = version // クライアントはこれを覚えておく🔑
});
});
app.MapGet("/orders/{orderId:guid}", (Guid orderId, HttpRequest http) =>
{
// クライアントが「私はここまで見えてるはず!」という最低バージョンを送る
var minVersionHeader = http.Headers["X-Session-Version"].ToString();
long.TryParse(minVersionHeader, out var minVersion);
// まずレプリカを見る(速い想定)
if (replica.TryGetValue(orderId, out var r))
{
if (r.Version >= minVersion)
return Results.Ok(new { source = "replica", order = r });
// レプリカが古い!→ ユーザー体験を守るため primary へフォールバック✨
if (primary.TryGetValue(orderId, out var p))
return Results.Ok(new { source = "primary(fallback)", order = p });
}
// レプリカにまだない場合でも、primary にはあるかも
if (primary.TryGetValue(orderId, out var latest))
return Results.Ok(new { source = "primary", order = latest });
return Results.NotFound();
});
app.Run();
record PlaceOrderRequest(string UserId, string MenuId);
record OrderRecord(
Guid OrderId,
string UserId,
string MenuId,
string Status,
DateTimeOffset CreatedAt,
long Version
);
これで何が嬉しいの?😊💡
- レプリカが遅れてても、注文した本人だけは「見えない😱」を回避できる
- つまり「ユーザー単位の整合性(Read-your-writes)」をアプリ側の工夫で作れる✨
10.9 AI活用(そのままコピペOK)🤖💬✨
- 「CampusCafeの各画面について、必要な整合性(強/ユーザー単位/弱)を分類して。理由はユーザーの気持ちで書いて」
- 「注文履歴画面で“見えたり消えたり”が起きるパターンを3つ出して。防ぐ保証(RYW/Monotonic reads)もセットで」
- 「在庫の“表示”と“確保”を分けた要件文を、短くわかりやすく整えて」
10.10 理解チェック✅🧠
- 「注文直後に履歴に出ない」がヤバいのは、どの保証が欲しいから?✍️➡️👀
- 「見えたはずの注文が次の更新で消える」系は、どの保証で防ぎたい?📈🔁
- 在庫の「表示」と「確保」は、同じ整合性で考えていい?📦🤔
- “全部を強整合にする”のデメリットを1つ言える?🐢⛔
- 「ユーザー単位の整合性」を守るために、アプリ側でできる工夫を1つ挙げてみて🔑✨
まとめ🌸✨
- 整合性は 一種類じゃない!「誰にとって」「どの画面で」必要かで決まる🌈
- まずは ユーザー単位の整合性(自分の操作が見える・古く戻らない)を意識すると、UXが一気に良くなる👤✅
- 次章では、この「期待」を 仕様として書く(即時必須/遅延OKを決める) ほうに進むよ📝🎯
(参考:C#は 2025年11月に C# 14 がリリース、.NET も 2025年11月に .NET 10 がリリースされ、2026年1月時点でも最新版系として配布・更新されているよ📦✨) (Microsoft Learn)