構成図とトレードオフ — 何を描き、何を省くか
設計を人に説明する場面では、たいてい図を描くことになります。しかし図そのものは設計ではありません。
同じ要件に対して、成立する構成は複数あります。図が示すのはそのうちの 1 つで、**なぜそれを選んだのかは図に描かれていません。**説明の中身は、選んだ案と捨てた案の比較です。図はその比較を支える道具として描きます。
この章で学ぶこと
- 箱と線が何を表すかを決めること
- 要件と箱を対応づける手順
- 描かないものの選び方
- mermaid での書き方
- 同じ要件に複数の正解があること (書き込み時に配るか、読み取り時に集めるか)
- 可用性・スケーラビリティ・一貫性の 3 軸で自分の図を点検する
この章で扱わないこと
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 台構成、あるいは最小構成。ここから何が足りないかを示すと、足す部品に理由が付きます
- 足りない箇所に部品を足す — 部品ごとに「何のために足したか」を 1 行で言う
- 捨てた案を 1 つ挙げ、なぜ捨てたかを言う — ここが説明の中心です。比較が無いと、選択の理由が示せません
- 諦めたものを明示する — 「一覧の反映は数秒遅れる」「フォロワーが多いアカウントは別扱い」など。トレードオフを言えることが理解の証明です
**5 が最も差が出ます。**構成を並べるだけなら暗記でもできますが、捨てた案を語るには両方を理解している必要があります。
時間や字数が限られるなら、**1・3・5 を優先します。**数字と、最小構成からの差分と、捨てた案。この 3 つがあれば設計の説明として成立します。
よくある誤解
「構成図を描くのが設計」 — 図は結論の表現です。設計は、複数の候補を比べて 1 つを選ぶ過程にあります。図だけを見せると、選ばなかった案が見えません。
「複雑な構成のほうが評価される」 — 要らない部品は、可用性を下げ (可用性と冗長化 の直列の積)、運用の負担を増やします。数字に対して過剰な構成は、理解していないことの表れと読まれます。
「トレードオフは弱点を認めること」 — 何を優先したかの表明です。「一貫性より可用性を優先し、閲覧数は古い値が出ることがある」は欠陥の告白ではなく、要件に沿った選択の説明です。
「図に全部描いたほうが親切」 — 読めなくなります。答えたい問いに関係する要素だけを描き、省いたものは省いたと書きます。
確認問題
問 1. 次の構成図をレビューします。指摘すべき点を挙げてください。「利用者 → ロードバランサー → アプリ 3 台 → データベース 1 台。アプリはセッションをメモリに保持」
問 2. 通知の一覧機能を設計します。「書き込み時に配る」と「読み取り時に集める」のどちらを選びますか。判断に必要な情報を挙げてください。
判断に必要なのは 3 つです。
- 読み書きの比 — 通知の発生回数と、一覧を開く回数の比。開く回数が多いなら書き込み時に配る側が有利
- 配る相手の数の分布 — 1 件の出来事が何人に通知されるか。全員宛の通知があるなら、書き込み時に配ると大量の書き込みになる
- 遅れが許されるか — 読み取り時に集める方式は、開くたびに処理が走るので表示が遅くなりうる
通知は「配る相手が事前に分かる」ことが多いので、書き込み時に配る方式と相性が良いです。ただし「全員に通知」のような機能があるなら、そこだけ別扱いにします。
片方に決め切らず、条件で分けるのが実務的な答えです。
問 3. 設計の説明で「可用性を重視した構成にしました」とだけ書かれていました。何を足しますか。
3 つ足します。
- 数字 — どの水準の可用性を目標にしたのか。年間何分の停止まで許容するのか。目標が無いと「重視した」の程度が伝わりません
- 何を諦めたか — 可用性のために犠牲にしたもの。コスト、書き込みの速さ、一貫性のいずれか。トレードオフの片側しか書かれていないのが問題です
- 捨てた案 — 検討した別の構成と、それを選ばなかった理由
とくに 2 が重要です。何かを重視すれば、必ず何かを犠牲にします。「可用性を重視し、代わりに書き込みの応答が数十ミリ秒遅くなることを受け入れた」と書けば、選択として読めます。
まとめ
- 箱と線が何を表すかを先に決めます。状態を持つものは形を変え、非同期の線は分けます
- **箱には存在理由があります。**言えない箱は消す候補で、要件に対して担う箱が無いなら足りていません
- 省く判断のほうが難しいので基準を決めます。省いたものは省いたと書きます
- 同じ要件に複数の構成が成り立ちます。読み書きの比と分布が選択を決めます
- 可用性・スケーラビリティ・一貫性の 3 軸で点検します。全部を満たすのではなく、優先と断念を言えるようにします
- 記述で答えるときは、数字 → 最小構成 → 足りない箇所 → 捨てた案 → 諦めたものの順で組み立てます
- **設計の説明はトレードオフの説明です。**構成の羅列は説明になりません