Skip to main content

第04章:Sagaの超ざっくり定義(進む+戻す)🧩🔁

Forward flow and Compensation (Undo) flow.

4.1 今日のゴール🎯✨

この章が終わると、こんな感じで説明できるようになります😊

  • Sagaって何?👉「一連のステップ」+「失敗したら帳尻合わせ(補償)」のセットだよ🙆‍♀️
  • 「戻す」って何?👉 DBのロールバックじゃなくて、補償トランザクション(後から取り消し操作)だよ🧾🔁
  • ざっくりC#で「進む→失敗→戻す」をミニ実装して、動きの感覚をつかむよ💻✨

4.2 Sagaの定義(超ざっくり)📌

Saga(サーガ)は、分散システム(複数サービス・複数DB・外部APIなど)で、 「サービスごとのローカルトランザクション」を順番に進めていくパターンです🚶‍♀️🚶‍♂️

そして途中で失敗したら… すでに成功した分を、補償トランザクションで順番に“帳尻合わせ”していくのがポイントです🧾✨ この考え方で、全体としてデータ整合性(最終的に筋が通る状態)を保ちます🛡️ (Microsoft Learn)


4.3 「進む」フローと「戻す」フロー(図でイメージ)🗺️🧠

例:ECの「注文→決済→在庫→配送」🛒💳📦🚚

成功と失敗(返金)の流れのイメージ 🗺️🧠

こんなふうに「成功した分を逆順で帳尻合わせ」していきます🔁💡


4.4 ここ超重要:「ロールバック」と「補償」は別モノ🙅‍♀️🧾

DBトランザクションのロールバック

  • 同じDBの中で「やっぱナシ!」って戻す感じ💾↩️
  • 速いし確実(そのDBの中だけなら)✨

Sagaの補償トランザクション

  • “後から”取り消し操作をする感じ🧾🔁
  • しかも補償自体も「いつか成功する」世界(=最終的整合性)🌙
  • 補償も失敗することがあるので、再開・再試行・冪等が大事になるよ🛡️ (Microsoft Learn)

4.5 「逆操作ができない」こと、普通にある😵‍💫

補償は、数学みたいに「完全に元に戻す」とは限りません🙅‍♀️ 現実にはこういうのがあるあるです👇

  • 返金はできるけど、手数料が戻らない
  • メール送信は取り消せない(代わりに謝罪メール…📩🙏)
  • 配送開始後は止められない(代替:返品受付・返金・クーポン🎁)

だから補償は「逆操作」よりも、**帳尻合わせ(ユーザーと会社の被害を最小化)**って感覚が近いです🧠✨


4.6 ミニ演習①:補償を“1行”で書いてみよう📝😊

下の表の「補償(戻す)」を、まずは1行で埋めてみてください💪✨ (完璧じゃなくてOK!「まず案を出す」が大事💡)

ステップ(進む)補償(戻す:帳尻合わせ)
注文作成(OrderCreated)🛒例:注文をキャンセル状態にする
決済確定(PaymentCaptured)💳例:返金する(Refund)
在庫確保(InventoryReserved)📦例:在庫予約を取り消す
配送手配(ShipmentCreated)🚚例:配送をキャンセル(可能なら)

🧠コツ:補償は「ユーザー体験」もセットで考えると強いです😊✨


4.7 ミニ演習②:C#で“超ミニSaga”を書いて動きを掴む💻🔁

ここでは「本物の決済や在庫」じゃなくて、コンソールで流れだけ再現します🎮 (動きの理解がゴールなので、まずはミニでOK🙆‍♀️)

参考:2026年1月時点では .NET 10 / C# 14 が最新ラインです(.NET 10.0.2 が 2026-01-13 リリース)(Microsoft Learn) Visual Studio 2026 は .NET 10 をフルサポートします。(Microsoft for Developers)

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

record SagaStep(string Name, Func<Task> Forward, Func<Task> Compensate);

class Program
{
static async Task Main()
{
Console.WriteLine("=== Mini Saga: 進む → 失敗 → 補償 ===");

var steps = new List<SagaStep>
{
new("① 注文作成", async () =>
{
await Task.Delay(200);
Console.WriteLine("✅ 注文作成 OK");
},
async () =>
{
await Task.Delay(200);
Console.WriteLine("↩️ 補償: 注文キャンセル");
}),

new("② 決済確定", async () =>
{
await Task.Delay(200);
Console.WriteLine("✅ 決済確定 OK");
},
async () =>
{
await Task.Delay(200);
Console.WriteLine("↩️ 補償: 返金処理");
}),

new("③ 在庫確保", async () =>
{
await Task.Delay(200);
Console.WriteLine("✅ 在庫確保 OK");
},
async () =>
{
await Task.Delay(200);
Console.WriteLine("↩️ 補償: 在庫予約取消");
}),

new("④ 配送手配(ここで失敗させる)", async () =>
{
await Task.Delay(200);
throw new Exception("配送APIがタイムアウト💥");
},
async () =>
{
await Task.Delay(200);
Console.WriteLine("↩️ 補償: 配送キャンセル(可能なら)");
}),
};

try
{
await RunSagaAsync(steps);
Console.WriteLine("🎉 Saga 全部成功!");
}
catch (Exception ex)
{
Console.WriteLine($"🧯 Saga 失敗: {ex.Message}");
}
}

static async Task RunSagaAsync(List<SagaStep> steps)
{
var completed = new Stack<SagaStep>();

foreach (var step in steps)
{
Console.WriteLine($"\n➡️ {step.Name} を実行…");

try
{
await step.Forward();
completed.Push(step);
}
catch
{
Console.WriteLine("💥 失敗!補償を逆順で実行するよ…🔁");

while (completed.Count > 0)
{
var done = completed.Pop();
Console.WriteLine($"⬅️ {done.Name} の補償…");

try
{
await done.Compensate();
}
catch (Exception cex)
{
// ここは後の章で「冪等性」「再試行」「手動介入」に繋がるポイント!
Console.WriteLine($"⚠️ 補償も失敗: {cex.Message}");
}
}

throw;
}
}
}
}

✅ポイント

  • 進む:Forward を順番に実行🚶‍♀️
  • 失敗:例外が出たらストップ🧯
  • 戻す:成功済みの分だけ、逆順で Compensate を実行🔁

4.8 よくある勘違い・事故ポイント集🚧😇

  • 「補償=完全に元通り」ではない🙅‍♀️(帳尻合わせ)
  • 「補償は必ず成功」ではない😱(だから後で冪等性が超重要になる)(Microsoft Learn)
  • 「補償を2回やる」事故が起きやすい⚠️(リトライ・重複メッセージで発生)
  • 「キャンセルできない操作」を前に置くと地獄になりやすい🔥(順番設計が大事)

4.9 AI活用(Copilot / Codex)🤖✨

「補償案を増やす」「危険な点を見つける」にAIがめちゃ強いです💪

① 補償案を複数出してもらう🧠💡

  • 「注文→決済→在庫→配送のSagaで、各ステップの補償案を3つずつ出して。ユーザー体験が良い順に並べて」

② “取り消せない操作”を洗い出す🕳️😵‍💫

  • 「このフローで“後から取り消せない/取り消しづらい”操作を列挙して、代替の補償案も提案して」

③ ミニSagaコードの改善を頼む🛠️✨

  • 「このC#のミニSagaを、ログが読みやすくなるように整形して。例外メッセージも分かりやすくして」

④ 章のミニ演習の答え合わせ✅

  • 「この表の補償案、業務的におかしいところある?リスクと改善案をセットで教えて」