第04章:前提② 非同期ってなに?(なんで必要?)⏱️🧩
この章のゴール 🎯✨
- 「同期(待つ)」と「非同期(待たない)」の違いを、感覚でつかむ🙂
- 非同期が必要になる理由(遅い・落ちる・混む)を、現実の失敗込みで理解する😇💥
- 「失敗や遅延がある世界」で壊れにくくするための、基本の考え方を覚える🧯🔁
1) 同期(待つ)と非同期(待たない)って?⚖️
✅ 同期(synchronous)
「処理Aが終わるまで、次に進まない」🧍♀️⏳
例:注文確定ボタン → メール送信が終わるまで画面が待つ📩⌛
- 👍 分かりやすい
- 👎 遅い処理があると、全部が遅くなる
- 👎 外部が落ちると、巻き込まれて失敗しやすい
✅ 非同期(asynchronous)
「処理Aは“お願いだけ”して、次に進む」🏃♀️💨 例:注文確定ボタン → “送る予定”を記録してすぐ完了 → メール送信は後で📦📤
- 👍 画面が速い(待たない)✨
- 👍 外部が落ちても、業務は守りやすい🛡️
- 👎 “あとでやる”分、設計が必要(ここが腕の見せ所)🧠🔧
2) 「非同期が必要」になる3つの現実 😇🌧️
現実①:外部サービスは遅い(しょっちゅう待たされる)🐢
メール、決済、配送、通知、他社API… 自分のアプリの外は、だいたい遅い&不安定になりがち📡⚠️
現実②:外部サービスは落ちる(たまにじゃない、普通にある)💥
- 一時的なネットワーク断🌧️
- 相手側の障害🧯
- たまたまタイムアウト⌛
「落ちる前提で設計する」が強い💪
現実③:アクセスは波がある(混むと全部が詰まる)🌊
同期で外部呼び出しを抱えると、混雑時にこうなりがち👇
- リクエストが詰まる🚧
- タイムアウト増える⌛💥
- 再試行でさらに混む🔁🔥(地獄ループ)
3) 同期でやると起きやすい“事故”を見てみよう😵💫💥
例:注文確定 → 「注文DB更新」+「メール送信」
❌ 同期パターン(巻き込まれやすい)
- 注文DB更新 ✅
- メール送信 ❌(タイムアウト)
- 画面はエラー表示 😭
- でもDBは更新済み…?(設計次第でねじれる)🌀
[ユーザー] ──注文確定──▶ [API]
[API] ──DB更新(成功)──▶ [DB] ✅
[API] ──メール送信──▶ [外部メール] ❌(遅延/失敗)
[API] ──エラー返す──▶ [ユーザー] 😭
ここで起きるのが、まさにOutboxが防ぎたい世界観📦🛡️ (「業務更新は成功したのに、通知だけ落ちた」みたいなやつ)📭
4) 非同期にすると“何が嬉しい”の?🌈✨
✅ ① 画面が速くなる(待たない)⚡
ユーザー体験が良くなる🙂✨ 「送信」ボタン押して10秒固まるの、つらいよね🥹
✅ ② 業務を守れる(外部の障害から切り離す)🛡️
外部が落ちても、注文確定そのものは成立させやすい✅ 通知は「あとで確実に送る」にできる📦📤
✅ ③ リトライ設計がしやすい(失敗は“あとで回収”)🔁
一時的に失敗したら、あとで再送すればOK🙆♀️ この「再送前提」が超重要😇
5) 「非同期=別スレッド?」って思いがち問題🧠🌀
よくある誤解👇
- 「非同期にしたらCPU処理が速くなる!」🚀(←だいたい違う)
- 「非同期=並列で勝手に動く!」👯♀️(←設計しないと勝手には守ってくれない)
ここでの“非同期”は主にこういう意味🙂
- 待ち時間(I/O)を抱えない(外部呼び出し・ネットワーク・DBなど)📡
- 処理を分ける(今やる/あとでやる)✂️
- 失敗の回収ルートを用意する(リトライ・隔離)🧯
6) リトライ前提の心構え 😇🔁(ここが最重要)
非同期世界では、こう考えるのが強い👇
✅ 失敗は「ゼロにする」より「回収する」🧯
- 失敗は起きる(前提)
- 起きたら、再実行できる設計にする
- それでもダメなら、人が直せる場所に隔離する(後の章でDead Letter)📮
✅ 失敗は大きく3種類で考えるとラク🙂
- 一時的:ネットワーク不安定、相手が一瞬落ちた🌧️ → リトライで治る可能性大
- 恒久的:データ不正、送信先が存在しない🧱 → 何回やっても無理
- 仕様的:その状態では送っちゃダメ🚫 → ロジック修正が必要
7) “Outboxにつながる”非同期の基本パターン📦📨
「今やること」と「あとでやること」を分ける✨
- 今:注文を確定する🛒✅
- 今:“送る予定”を保存する📦🧾
- あとで:保存した“送る予定”を拾って送る📤⏱️
(今) [API] ──注文DB更新+送る予定を保存──▶ [DB] ✅✅
(今) [API] ──すぐ成功返す──▶ [ユーザー] 🙂✨
(あと) [送信係] ──送る予定を取得──▶ [DB]
(あと) [送信係] ──外部へ送信──▶ [外部] 📩
この「送る予定」こそが Outbox の芯📦🧠 (まだ深掘りしないけど、“非同期の考え方”としてここで覚えるのが大事)✨
8) ミニコードで感覚をつかむ🧪✨(TypeScript)
❌ 同期で外部通知まで待つ例(遅い&落ちると巻き込まれる)😵💫
async function placeOrderAndNotify(input: { userId: string; items: string[] }) {
// 1) 注文を保存(成功したとする)
const orderId = await saveOrderToDb(input);
// 2) 外部通知(ここが遅い・落ちる…)
await sendEmailViaExternalService({
toUserId: input.userId,
subject: "注文確定",
body: `注文ID: ${orderId}`,
});
return { orderId };
}
この関数の弱点👇
- 外部通知が遅いと、注文処理全体が遅い🐢
- 外部通知が失敗すると、注文自体を失敗扱いにしがち😱
- “成功したのに失敗に見える”が起きやすい🌀
✅ 非同期で「送る予定」を残す例(“あとで送る”)📦📤
type OutboxEvent = {
id: string; // 例: UUID
eventType: "OrderPlaced";
payload: unknown; // 後の章で「壊れにくいJSON」にする
createdAt: string; // ISO文字列など
};
async function placeOrder(input: { userId: string; items: string[] }) {
// ここでは「注文保存」と「Outbox追加」をセットでやる(後の章で深掘り)
const result = await runInTransaction(async (tx) => {
const orderId = await tx.saveOrder(input);
const evt: OutboxEvent = {
id: crypto.randomUUID(),
eventType: "OrderPlaced",
payload: { orderId, userId: input.userId },
createdAt: new Date().toISOString(),
};
await tx.insertOutbox(evt);
return { orderId };
});
// ここで外部通知はしない!すぐ返す🙂✨
return result;
}
ポイントはこれ👇
- 「外部に送る」は 別の“送信係” がやる📤
- 送信係は、Outboxから拾って送る🔁
- 失敗してもリトライできる(設計できる)🧯
9) “送信係”ってどんなもの?👩🏭📤(イメージだけ)
世の中には「非同期実行」のための選択肢が色々あるよ🙂✨
- Redisを使ったジョブキュー(例:BullMQ)📮⚙️(最近も継続的にリリースあり)(GitHub)
- ワークフロー基盤(例:Temporal の TypeScript SDK)🧬⏱️(SDKも更新が続いてる)(GitHub)
この教材ではまず「考え方」を固めて、あとで実装に落としていくよ🪜✨
10) 🤖 AI活用ミニコーナー(コピペで使える)✨
✅ 「同期→非同期」に分ける相談プロンプト📝
あるユースケースの処理手順を貼るので、 “今やるべき処理” と “あとでやるべき処理(非同期)” に分けて、理由も添えてください。 さらに、外部失敗時のリトライ方針(回数・間隔・恒久失敗の扱い)も提案してください。
✅ 「失敗パターン洗い出し」プロンプト🧯
この外部連携(メール送信/通知/他社API呼び出し)で起こり得る失敗を、 一時的/恒久的/仕様的 の3種類に分類して、対処案を箇条書きで出してください。
11) 最新事情メモ(この章に関係するところだけ)🗓️✨
- TypeScript のリリースノートは 5.9 系として更新が続いているよ(ドキュメント更新日が 2026-02-02)(TypeScript)
- Node.js は 24 系が Active LTS、22/20 系が Maintenance LTS など、複数ラインが並走してる(2026年1月時点のステータス表)(Node.js)
まとめ 🎀✅
- 非同期は「待たない」だけじゃなく、外部の失敗を業務から切り離すための武器🛡️
- 失敗は起きる前提で、回収(リトライ・隔離)できる設計にするのが強い🧯🔁
- Outbox は「あとで送る」を成立させるための、超実用的な土台📦✨