01 / 10 · SYSTEM DESIGN

設計の基礎:要件から境界を引く

性能、状態、権限、障害を手掛かりに、システムの分け方を考えます。

内容の確認日:

この章で考える問い

どの箱を描く前に、何を決めるべきか?

まず図全体の流れを見て、細部は「原寸で読む」を開いて確認してください。原寸の図は横にスクロールできます。正常な流れの次に、失敗時の分岐を追いましょう。

箱を描く前に成功条件を書く

システムデザインは技術名の配置ではなく、制約の中で利用者の操作を成立させる仕事です。この章のAIチャットは学習用の概念設計であり、Kumyuの稼働構成を示すものではありません。まず「送った質問を失わない」「他人の会話を読ませない」「混雑しても遅延を説明する」と書きます。そこから保存、権限、実行の責任を分けます。

仮の要求を「月間の有効な送信の99.9%を受理」「最初の応答表示までp95が2秒以内」とします。これは実測値でも製品保証でもありません。対象ユーザー、計測の始点と終点、集計期間、拒否や障害の扱いを一緒に決めます。Google SREのSLO解説では、SLIをサービスの測定値、SLOをその目標として区別しています。

平均が速くても一部の人が長く待つ場合があります。p95は観測値の95%がその値以下という分位点であり、平均とは異なります。応答開始と回答完了も別の指標にします。長文を返す操作と短い操作を混ぜれば、数値の変化の意味を見失います。

クライアント、認証と認可、実行処理、保存先の責任境界
原寸で読む(横にスクロール)
クライアント、認証と認可、実行処理、保存先の責任境界
要件から状態の所有者を決める 原寸の図を開く ↗

図の矢印は要求と保存の経路を表します。重要なのは箱の数より、どの箱が最終状態を決定するかという点です。

要求、状態、権限を分ける

認証(authentication)は誰が操作しているかを確認します。認可(authorization)はその人が対象の会話を読めるか、変更できるかを判断します。ログイン済みでも任意のconversation IDを読めてはいけません。受付、結果取得、再接続の各入口で所有権や所属を確認します。

状態の所有者は一つにします。画面は一時的な表示、会話DBは確定したメッセージ、実行レコードは進行状態を持ちます。同じ「完了」を複数のサービスが独立に書き換えると、片方だけ成功した状態を説明できません。DBトランザクションでまとめられる変更と、外部ネットワークの先の処理を区別しましょう。外部ツールが成功した直後に通信が切れれば、DBをロールバックしても外部の変更は戻りません。

送信の時系列を追う

要求送信、本人確認、読み書き、永続化、応答、計測の順序
原寸で読む(横にスクロール)
要求送信、本人確認、読み書き、永続化、応答、計測の順序
画面の受信と永続化を区別する 原寸の図を開く ↗

受理済みとは何かを決めます。ここでは、メッセージと実行予定を永続化できた状態とします。画面へ返す前に保存するので、応答だけ失われても同じ要求IDで結果を照会できます。外部処理には別の実行IDとツール呼び出しIDを割り当て、要求の再送と外部操作の再実行を混同しません。

容量は単位を揃えて見積もる

安定した系の長期平均ではLittleの法則 L = λ × W が使えます。Lは系の中にいる平均件数、λは単位時間当たりの到着・処理率、Wはその系で過ごす平均時間です。仮に毎秒20件、平均滞在時間3秒なら平均60件が処理中または待機中になります。これはキューの待ち件数だけでも、必要CPU数でもありません。p95の時間を平均Wの代わりに入れて厳密な同時数を得ることもできません。

この概算から、どの待ちが長いかを測ります。モデル待ちが支配的なら、WebサーバーのCPUを増やしても改善は難しいでしょう。DB待ちが増え続けるなら、無制限に受け入れる前に同時実行上限や拒否条件が必要です。到着が処理能力を上回り続ける系に、安定時の式をそのまま当てはめません。

壊れ方を先に設計する

流入増加から待機列と締切超過が発生し、受付制限と負荷拒否で抑える経路
原寸で読む(横にスクロール)
流入増加から待機列と締切超過が発生し、受付制限と負荷拒否で抑える経路
過負荷と締切を制御する 原寸の図を開く ↗

図は過負荷から締切超過へ進む経路です。受付件数と待機時間を制限して、処理能力を超える要求を断ります。そのうえで、保存や外部呼び出しの失敗は境界ごとに回復させます。

トランザクションの失敗が確定した場合は未受理として再送を案内します。コミット応答がタイムアウトしただけなら、保存の成否は不明なので、元の要求IDで照会してから次の操作を決めます。保存成功後の切断なら既存の実行を照会します。外部操作の結果が不明なら、成功とも失敗とも決めず、照会や照合へ進めます。何でも最初からやり直す実装は、回答や注文を二重に作るおそれがあります。

観測にはリクエストID、実行ID、ツール呼び出しID、処理時間、状態遷移、エラー分類を使います。会話本文、認証情報、検索結果の全文をそのままログへ入れません。IDをたどれば「受付は速いが外部処理で待つ」と説明できるようにします。利用者の体感を確認する計測と、内部原因を絞る計測は両方必要です。

最初は小さく、分割は測定から

選択 得られるもの 設計上の負担
モジュールを分けた単一アプリ 呼び出しと配備が単純 一部だけ拡張しにくい
独立したサービス 別々の容量・障害範囲 通信失敗、認可、運用の増加
キャッシュ 同じ読取の繰り返しを減らす 古い値と無効化の管理

単一アプリでも内部の責任は分けられます。まず読取回数、遅延、変更頻度を測り、独立して拡張する理由ができた部分を分割します。キャッシュはDBの代わりではありません。たとえばプロフィールの版番号をキーに含め、更新後は新しい版を読みます。権限が変わる情報はTTLだけを頼りにせず、どこで最新の権限を検証するかを決めます。

設計例:最初のAIチャット

一つのアプリ内に受付・実行・配信のモジュールを置き、DBでメッセージと実行状態を管理します。長時間処理だけを非同期実行へ渡し、表示はリアルタイム配信で更新します。制約の強い場所から境界を増やすと、必要な複雑さの理由を説明しやすくなります。

練習と解答

仮に毎秒10件、平均滞在時間4秒の処理を、平均1秒へ短縮しました。同じ安定負荷なら平均滞在件数はいくつでしょうか。答えは40件から10件になります。ただしCPU負荷や最大同時数が4分の1になるとは限りません。次に「保存後にブラウザーだけ切断した」場合の回復を描いてみましょう。解答は同じ実行IDの状態取得であり、新しい実行の無条件作成ではありません。

押さえたいポイント

利用者の成功条件を測定可能な要件に変え、状態の所有者と失敗時の動きを決める。

出典・さらに読む

  1. Google SRE: Service Level Objectives内容の確認日: 2026-10-23

学習と設計検討のための概念的な構成例です。稼働中のKumyuの実装を表す図ではありません。

← 全章の一覧へ