メインコンテンツまでスキップ

構成図とトレードオフ — 何を描き、何を省くか

設計を人に説明する場面では、たいてい図を描くことになります。しかし図そのものは設計ではありません。

同じ要件に対して、成立する構成は複数あります。図が示すのはそのうちの 1 つで、**なぜそれを選んだのかは図に描かれていません。**説明の中身は、選んだ案と捨てた案の比較です。図はその比較を支える道具として描きます。

この章で学ぶこと

  • 箱と線が何を表すかを決めること
  • 要件と箱を対応づける手順
  • 描かないものの選び方
  • mermaid での書き方
  • 同じ要件に複数の正解があること (書き込み時に配るか、読み取り時に集めるか)
  • 可用性・スケーラビリティ・一貫性の 3 軸で自分の図を点検する
前提知識

第 11 部のここまで (規模の見積もりスケールの方向可用性と冗長化データの分散) を前提にします。図に描く要素がそこで出てきたものだからです。

この章で扱わないこと

UML やその他の記法体系の網羅的な説明は扱いません。作図ツールの比較も行いません。mermaid の記法そのものは 記法リファレンス が扱います。

箱と線が何を表すか

構成図が読みにくくなる原因の多くは、種類の違うものが同じ形で描かれていることです。最初に決めます。

図の要素表すもの決めておくこと
動いているもの (プロセス、サービス)1 つの箱が 1 台なのか、複数台のまとまりなのか
円筒状態を持つもの (データベース、キャッシュ、オブジェクトストレージ)箱と形を変える。壊れたときの影響が違うため
実線同期の呼び出し (応答を待つ)向きは呼ぶ側から呼ばれる側へ
点線非同期の伝達 (応答を待たない)キュー経由、ログ送信など
境界 (ネットワーク、アカウント、データセンター)越えるときに何が変わるか

箱と円筒を分ける理由は、増やし方が違うからです。状態を持たない箱は台数を増やせますが、状態を持つものは複製か分割が要ります (スケールの方向)。形が違えば、図を見た人が「ここは簡単には増やせない」と分かります。

実線と点線を分ける理由は、壊れたときの伝わり方が違うからです。同期の呼び出しは、相手が遅いと自分も遅くなります。非同期なら切り離せます。

要件と箱を対応づける

**構成図の箱は、それぞれ理由があって存在します。**その理由を言えない箱は、消す候補です。

何のために置いたか
ロードバランサー台数を増やせるようにする + 落ちた台を配り先から外す
キャッシュ読み取りをデータベースまで届かせない
キュー書き込みの山を平らにする + 応答を早く返す
レプリカ読み取りを分散する + プライマリが落ちたときの継続

この対応を書き出すと、**要らない箱が見つかります。**読み書きの比を見たら書き込みが毎秒 4 件しかないのにキューを置いている、というような設計は、規模の見積もり の数字と突き合わせれば分かります。

逆に、要件はあるのに担う箱が無い箇所も見つかります。「止まってはいけない」と言っているのに 1 個しかない箱がある、という形で現れます。

描かないもの

図に全部を描くと読めなくなります。省く判断のほうが難しいので、基準を決めます。

描くものは、その図で答えたい問いに関係する要素だけです。「負荷をどう捌くか」の図に、監視やログ収集の線は要りません。

省いてよいもの理由
監視・ログ・シークレット管理どの構成でも同じように付く。別の図にする
内部のクラス構造層が違う。構成図は台とプロセスの粒度で描く
具体的な製品名 (議論の段階では)「キャッシュ」で足りる。製品の選定は後の判断
正常時にしか通らない経路の詳細主要な流れを描き、例外は文章で補う

**省いたものは「描いていない」と明記します。**図に無いことが「存在しない」と読まれると、誤解のもとになります。

mermaid での書き方

この repo の図は mermaid で書きます。構成図では flowchart を使い、境界を subgraph で囲みます。

書くときの型は 3 つです。

  • 上から下、または左から右へ流れを揃える (TB / LR)。向きが混ざると追えなくなります
  • 状態を持つものは [( )] で円筒にする。増やし方が違うことが一目で分かります
  • 非同期の線は -.-> にし、ラベルで何が流れるかを書く

実際の構成に近い例は AWS 実践ガイド のトップにあります。ネットワークの境界を入れ子の subgraph で表現しています。

同じ要件に複数の正解がある

設計の説明がトレードオフの説明になるのは、正解が 1 つではないからです。例で見ます。

要件は「フォローしている人の投稿を、新しい順に一覧で表示する」です。2 つの構成が成り立ちます。

書き込み時に配る

投稿が行われた時点で、フォロワー全員の一覧へ書き込みます。

  • **読み取りが速い。**自分の一覧を順に読むだけで済みます
  • **書き込みが重い。**フォロワーが 10 万人いれば、1 回の投稿で 10 万件の書き込みが発生します
  • 容量を食います。同じ投稿がフォロワーの数だけ複製されます

読み取り時に集める

投稿は 1 か所に保存し、一覧を開いたときにフォロー先の投稿を集めます。

  • **書き込みが軽い。**1 件保存するだけです
  • **読み取りが重い。**開くたびにフォロー先を引き、それぞれの投稿を集めて併合します
  • 容量は小さくて済みます

どちらを選ぶか

読み書きの比が決めます (規模の見積もり)。一覧を開く回数が投稿の回数より圧倒的に多いなら、重い処理を書き込み側に寄せたほうが総量は減ります。

ただし**フォロワー数の分布が偏っていると、書き込み時に配る方式は破綻します。**100 万人のフォロワーを持つアカウントが 1 回投稿するたびに 100 万件の書き込みが走るからです。

**実務では混ぜます。**通常のアカウントは書き込み時に配り、フォロワーが極端に多いアカウントだけ読み取り時に集めて併合します。この「混ぜる」という判断自体が、トレードオフを理解していることの証明になります。

3 軸で自分の図を点検する

図が描けたら、3 つの軸で見直します。それぞれ「壊す質問」を持っています。

壊す質問見つかるもの
可用性この箱が落ちたら何が起きるか。1 個しかない箱はどれか単一障害点、縮退できない依存 (可用性と冗長化)
スケーラビリティ利用者が 10 倍になったら、どこが最初に詰まるか状態を持ったサーバー、増やせないデータストア (スケールの方向)
一貫性複製や分割をまたぐとき、利用者に古い値が見えるか書いた直後に読めない箇所 (データの分散)

**3 つとも満たす構成は、たいてい高価です。**だから点検の目的は全部を満たすことではなく、どれを優先し、どれを諦めたかを言えるようにすることです。

記述で答えるときの骨子

「この要件でシステムを設計してください」と文章で問われたときの組み立て方です。

  1. 要件を数字にする — 利用者数、リクエスト毎秒、読み書きの比、データ量。仮定なら仮定と書く (規模の見積もり)
  2. 機能要件と非機能要件を分ける — 何ができるか (機能) と、どれだけ速く・落ちずに・正しく動くか (非機能)。設計を分けるのは後者です
  3. 素直な構成を先に置く — 1 台構成、あるいは最小構成。ここから何が足りないかを示すと、足す部品に理由が付きます
  4. 足りない箇所に部品を足す — 部品ごとに「何のために足したか」を 1 行で言う
  5. 捨てた案を 1 つ挙げ、なぜ捨てたかを言う — ここが説明の中心です。比較が無いと、選択の理由が示せません
  6. 諦めたものを明示する — 「一覧の反映は数秒遅れる」「フォロワーが多いアカウントは別扱い」など。トレードオフを言えることが理解の証明です

**5 が最も差が出ます。**構成を並べるだけなら暗記でもできますが、捨てた案を語るには両方を理解している必要があります。

時間や字数が限られるなら、**1・3・5 を優先します。**数字と、最小構成からの差分と、捨てた案。この 3 つがあれば設計の説明として成立します。

よくある誤解

「構成図を描くのが設計」 — 図は結論の表現です。設計は、複数の候補を比べて 1 つを選ぶ過程にあります。図だけを見せると、選ばなかった案が見えません。

「複雑な構成のほうが評価される」 — 要らない部品は、可用性を下げ (可用性と冗長化 の直列の積)、運用の負担を増やします。数字に対して過剰な構成は、理解していないことの表れと読まれます。

「トレードオフは弱点を認めること」 — 何を優先したかの表明です。「一貫性より可用性を優先し、閲覧数は古い値が出ることがある」は欠陥の告白ではなく、要件に沿った選択の説明です。

「図に全部描いたほうが親切」 — 読めなくなります。答えたい問いに関係する要素だけを描き、省いたものは省いたと書きます。

確認問題

問 1. 次の構成図をレビューします。指摘すべき点を挙げてください。「利用者 → ロードバランサー → アプリ 3 台 → データベース 1 台。アプリはセッションをメモリに保持」

3 点あります。

指摘根拠
アプリがセッションをメモリに持っている3 台に振り分けられるので、リクエストごとに別の台へ届く。ログインが切れる (スケールの方向)
データベースが単一障害点1 個しかない箱。落ちればアプリが 3 台あってもサービスは止まる (可用性と冗長化)
ロードバランサーも単一障害点図では 1 個。冗長化されているかが図から読み取れない

あわせて、図から読み取れない情報も挙げます。「アプリ 3 台」が同じ建物にあるのか、別の区画に分かれているのかで可用性の計算が変わります。1 個の箱が 1 台なのか複数台のまとまりなのかを、図の凡例で決めておく必要があります。

問 2. 通知の一覧機能を設計します。「書き込み時に配る」と「読み取り時に集める」のどちらを選びますか。判断に必要な情報を挙げてください。

判断に必要なのは 3 つです。

  • 読み書きの比 — 通知の発生回数と、一覧を開く回数の比。開く回数が多いなら書き込み時に配る側が有利
  • 配る相手の数の分布 — 1 件の出来事が何人に通知されるか。全員宛の通知があるなら、書き込み時に配ると大量の書き込みになる
  • 遅れが許されるか — 読み取り時に集める方式は、開くたびに処理が走るので表示が遅くなりうる

通知は「配る相手が事前に分かる」ことが多いので、書き込み時に配る方式と相性が良いです。ただし「全員に通知」のような機能があるなら、そこだけ別扱いにします。

片方に決め切らず、条件で分けるのが実務的な答えです。

問 3. 設計の説明で「可用性を重視した構成にしました」とだけ書かれていました。何を足しますか。

3 つ足します。

  1. 数字 — どの水準の可用性を目標にしたのか。年間何分の停止まで許容するのか。目標が無いと「重視した」の程度が伝わりません
  2. 何を諦めたか — 可用性のために犠牲にしたもの。コスト、書き込みの速さ、一貫性のいずれか。トレードオフの片側しか書かれていないのが問題です
  3. 捨てた案 — 検討した別の構成と、それを選ばなかった理由

とくに 2 が重要です。何かを重視すれば、必ず何かを犠牲にします。「可用性を重視し、代わりに書き込みの応答が数十ミリ秒遅くなることを受け入れた」と書けば、選択として読めます。

まとめ

  • 箱と線が何を表すかを先に決めます。状態を持つものは形を変え、非同期の線は分けます
  • **箱には存在理由があります。**言えない箱は消す候補で、要件に対して担う箱が無いなら足りていません
  • 省く判断のほうが難しいので基準を決めます。省いたものは省いたと書きます
  • 同じ要件に複数の構成が成り立ちます。読み書きの比と分布が選択を決めます
  • 可用性・スケーラビリティ・一貫性の 3 軸で点検します。全部を満たすのではなく、優先と断念を言えるようにします
  • 記述で答えるときは、数字 → 最小構成 → 足りない箇所 → 捨てた案 → 諦めたものの順で組み立てます
  • **設計の説明はトレードオフの説明です。**構成の羅列は説明になりません
関連リファレンス

次に読む

  • 結合と集計 — ここから第 12 部。構成の話から、その中を流れるクエリそのものの読み書きへ移ります
  • リトライと冪等性 — 分散した構成で失敗をどう扱うか
  • インデックス — 分散させる前に、1 台の中でできることを確かめる