スケールの大きな物を設計する際の考え方をトランプのカードで説明する

ドワンゴでニコニコ生放送のWebフロントエンジニアをやっています、 @misuken です。

この記事では、スケールの大きな物を設計するときによくイメージしている、トランプのカードを例に複雑度を抑える考え方を紹介します。

[物の性質を突き詰めて設計に活かす考え方をトランプのカードで説明する](https://qiita.com/misuken/items/1547e63d6fd84218001c) ではトランプの中の小さな世界の話をしていますが、こちらはトランプの外の大きな世界を掘り下げる内容です。

当たり前と言えば当たり前のこと(再帰的な構造)だったりしますが、思いの外チームのメンバーに説明する機会があり、人に具体的に説明できる資料があると便利なので文書化することにしました。

## スケールと複雑度

世の中の物事は、スケールの大きな物になればなるほど複雑になります。
扱う物の種類が多かったり、情報量が増えるためで、スケールが大きくなったら複雑になることを避けることはできません。

しかし、スケールの大きな物でも同一視できる部分が見つかれば、複雑度を抑えることはできます。

### トランプのカード

あなたの目の前にトランプのカードが54枚が置いてあります。

どういう形で置いてあるでしょうか？

キレイに積まれた状態か、ドサッとバラバラに置かれている状態を想像する人が多いと思います。

キレイに積まれた状態とドサッとバラバラに置かれている状態では、どちらのほうが複雑でしょうか？

明らかにドサッとバラバラに置かれているほうが複雑に感じます。

なぜ複雑さに差が出るのでしょうか？

カード1枚1枚の情報に差はあれど、カードの型自体は同じです。
それが揃ってまとまっていると複数のカードを同一視できるからではないでしょうか。

カードが揃ってまとまっているときの複雑さは、カード1枚を扱うときの複雑さと対して差を感じないはずです。

キレイに積んで複雑さを最小限にしましょう。

### トランプの箱

目の前にトランプの箱が54個が置いてあります。

どういう形で置いてあるでしょうか？

キレイに積まれた状態か、ドサッとバラバラに置かれている形を想像する人が多いと思います。

身に覚えのある話ですね。

対象の単位がトランプのカードからトランプの箱に変わっただけです。

箱1個1個の収納状況に差はあれど、箱の型自体は同じで、それが揃っていると複数の箱を同一視できるからです。
複数の箱が揃っているまとまりの複雑さは、箱1個を扱う複雑さと対して差を感じません。

キレイに積んで複雑さを最小限にしましょう。

### 段ボール箱

目の前にトランプの箱が54個入った段ボール箱が54個があります。

また同じです。

キレイに積んで複雑さを最小限にしましょう。

### トラック

目の前に段ボール箱が54個入ったトラックが54両あります。

また同じです。

キレイに整列させて複雑さを最小限にしましょう。

### 船

目の前にトラックが運んだコンテナ54個を積んだ船が54隻あります。

また同じです。

キレイに整列させて複雑さを最小限にしましょう。

### 整然と並んでいる状態では複雑さを感じない

このように、トランプのカードから船までスケールしても、整然と並んでいれば複雑さを感じることはありません。
トランプのカードの枚数が `54^5 = 459165024` 約4.6億枚でも、複雑さを感じないのです。

整然と並んでいれば、船のコンテナの中がどうなっているかを容易に想像できるくらいシンプルです。

もし、コンテナがめちゃくちゃに積んであったり、船に直接トランプのカードや箱がバラバラに積んであったりしたら、頭を抱えることでしょう。

大きな物や複雑な物は、何かが繰り返されて構築されていると捉えることで、複雑度に対抗できるようになります。

## 責務の分割

同じことの繰り返しとは言っても、責務も全く同じである必要はありません。
同一視できる責務は横断的に、個々の独自の責務は分割された階層の內部に、それぞれが依存するスコープに合わせて責務を持てば問題ありません。

### 階層化で分割される責務

少し雑ですが、トランプから船までのそれぞれの責務は以下のようになります。  

- 子供がトランプのカードをトランプの箱に収める
    - 子供はトランプの箱に収めた内容を知っている
- 親がトランプの箱を段ボール箱に収める
    - 親は段ボール箱に収めた内容を知っているが、トランプの箱の内容は知らなくて良い
- トラックの運転手が段ボール箱をコンテナに収める
    - 運転手はコンテナに収めた内容を知っているが、段ボール箱の内容は知らなくて良い
- 船の船員がコンテナを船に収める
    - 船員は船に収めた内容を知っているが、コンテナの内容は知らなくて良い

4つのレイヤーで扱うものの粒度は違えど、やっていることは "人が物をスペースに収める" と同一視できます。  
扱う粒度が大きくなったとしても、同じことの繰り返しと捉えれば、同一視できるので複雑度は上がりません。

### レイヤーごとに分割される責務

それぞれのレイヤーでの物の収め方(収めるロジック)は違っても問題ありません。  
トランプのカードを箱に収めるために必要な知識と、船にコンテナを収めるために必要な知識は違います。
そこはレイヤーごとに閉じられた世界の責務です。

それぞれのレイヤー間の責務は干渉しないので、責務が閉じていると言えます。
そのレイヤーに足を踏み入れない限り、レイヤー内の責務を考える必要がありません。

今回はわかりやすくトランプの例を示しましたが、配送という視点で見れば、箱の中身自体はトランプに限らず、規定の箱に収まっていれば他のもの一緒に送れます。

各レイヤーの中では規定の箱に収めるという責務に集中すれば良いことがわかります。
システム全体としては責務の種類が多かったとしても、一度に考えることが少なければ、無駄に複雑度が上がることはありません。

## 同様の仕組みで考えられるもの

箱に入れて次の階層へ、箱から取り出して次の階層へ、といった同様の仕組みは色々なものを当てはめることができます。  

- HTTP 等のOSI参照モデル
  - 目的別に7つの階層で設計されています
- 郵送システム
  - 規模や役割(階層)の違う郵便局を経由して全国に配送されます
  - 国 都道府県 市区町村 番地 といった住所も階層で構築されています
- Webフロントのアプリケーション
  - ツリー状の階層にコンポーネントが連なった集合です

それぞれ、同じことの繰り返しと捉えることで複雑度を抑えられます。

同じことの繰り返しで細かいレイヤー(スコープ)に分割して、個々の違いはそのレイヤー(スコープ)の中で吸収します。  
全体の骨格が揺るがなければ、それぞれのスコープ内の調整は大きなビルの内装工事をするようなもので、安心して作業できます。

ビルの內部はさらに フロア 部屋 デスク 引き出し のように、住所の更に細かいビル内の住所と捉えることができます。  
デスクの骨格が揺るがないのであれば、引き出しの中も安心して整理できるといった具合です。

## レイヤーを表にしてみる

レイヤーごとに何をどうするのか、登場人物は誰か、といった情報を表に起こしてみると、より精度を上げられます。

以下の点を意識しながら調整していきます。

1. 表の形に揃わないところがないか
1. レイヤーの名前と扱っている責務が一致しているか
1. 対称性のない単語が使われているところがないか

### 表に書き出す

レイヤーごとに粒度の遷移と、場所の遷移があることがわかるので、まずは漠然と書き出してみます。  
Aさんの子供がBさんの子供にトランプのカードを送るイメージ。

通常は家の中のことまで考える必要はないと思いますが、今回はあくまで例ということで。

| 粒度の遷移 | レイヤー | 場所の遷移 |
|---|---|---|
| トランプのカード → トランプの箱 | 家庭(A)の部屋 | 家庭Aの子供部屋 → 部屋 |
| トランプの箱 → 段ボール箱 | 家庭(A) | 部屋 → 家 |
| 段ボール箱 → コンテナ | 陸運業者(A) | 家 → トラック |
| コンテナ → 船 → コンテナ | 海運業者 | トラック → 船 → トラック |
| コンテナ → 段ボール箱 | 陸運業者(B) | トラック → 家 |
| 段ボール箱 → トランプの箱 | 家庭(B) | 家 → 部屋 |
| トランプの箱 → トランプのカード | 家庭(B)の部屋 | 部屋 → 家庭Bの子供部屋 |

### 粒度と場所のスコープの調整

単なる "部屋" という存在が緩かったので、トランプのカードのスコープを子供部屋の床に直に置いて実際に使用している状況に揃え、粒度と場所のスコープが対応するようにしました。

- 運ぶ担当も暗黙的だったので明示
- 場所の遷移をどこから誰がどこに運んだかの形式に統一

| 粒度の遷移 | レイヤー | 場所の遷移 |
|---|---|---|
| トランプのカード → トランプの箱 | 家庭(A)の部屋 | 子供部屋の床 → 子供 → 子供部屋 |
| トランプの箱 → 段ボール箱 | 家庭(A) | 子供部屋 → 親 → 家 |
| 段ボール箱 → コンテナ | 陸運業者(A) | 家 → トラック → 港 |
| コンテナ → 船 → コンテナ | 海運業者 | 港 → 船 → 港 |
| コンテナ → 段ボール箱 | 陸運業者(B) | 港 → トラック → 家 |
| 段ボール箱 → トランプの箱 | 家庭(B) | 家 → 親 → 子供部屋 |
| トランプの箱 → トランプのカード | 家庭(B)の部屋 | 子供部屋 → 子供 → 子供部屋の床 |

### 歪(いびつ)な部分の調整と正規化

ある程度情報が揃って列が見えてきたのでカラムを増やして正規化しました。

- 発送する側と受け取る側のレイヤーが真ん中で分かれるのでフェーズとしてカラムに追加
- 海運業者のレイヤーが歪だったので送信と受信の関係に分割
- 担当をカラムに追加
- 粒度の遷移はレイヤーに対する入出力と捉えられるのでそれぞれをカラムに追加
- 場所の遷移は今ある場所から移動させた先なので所在地と移動先としてカラムに追加

| 入力 | 出力 | レイヤー | フェーズ | 担当 | 所在地 | 移動先 |
|---|---|---|---|---|---|---|
| トランプのカード | トランプの箱 | 家庭の部屋 | 送信 | 子供 | 子供部屋の床 | 子供部屋 |
| トランプの箱 | 段ボール箱 | 家庭 | 送信 | 親 | 子供部屋 | 家 |
| 段ボール箱 | コンテナ | 陸運業者 | 送信 | トラック | 家 | 港 |
| コンテナ | 船 | 海運業者 | 送信 | 船 | 港 | 海 |
| 船 | コンテナ | 海運業者 | 受信 | 船 | 海 | 港 |
| コンテナ | 段ボール箱 | 陸運業者 | 受信 | トラック | 港 | 家 |
| 段ボール箱 | トランプの箱 | 家庭 | 受信 | 親 | 家 | 子供部屋 |
| トランプの箱 | トランプのカード | 家庭の部屋 | 受信 | 子供 | 子供部屋 | 子供部屋の床 |

### 軸のブレているところを調整

担当の部分が人の種類と輸送手段が混ざっているので整理しました。

- 担当カラムを担当者と輸送手段に分離

| 入力 | 出力 | レイヤー | フェーズ | 担当者 | 輸送手段 | 所在地 | 移動先 |
|---|---|---|---|---|---|---|---|
| トランプのカード | トランプの箱 | 家庭の部屋 | 送信 | 子供 | 人 | 子供部屋の床 | 子供部屋 |
| トランプの箱 | 段ボール箱 | 家庭 | 送信 | 親 | 人 | 子供部屋 | 家 |
| 段ボール箱 | コンテナ | 陸運業者 | 送信 | 運転手 | トラック | 家 | 港 |
| コンテナ | 船 | 海運業者 | 送信 | 船員 | 船 | 港 | 海 |
| 船 | コンテナ | 海運業者 | 受信 | 船員 | 船 | 海 | 港 |
| コンテナ | 段ボール箱 | 陸運業者 | 受信 | 運転手 | トラック | 港 | 家 |
| 段ボール箱 | トランプの箱 | 家庭 | 受信 | 親 | 人 | 家 | 子供部屋 |
| トランプの箱 | トランプのカード | 家庭の部屋 | 受信 | 子供 | 人 | 子供部屋 | 子供部屋の床 |

### 単語の調整

最後に少し違和感があったのでカラムの名前を整理しました。

- 所在地と移動先をより配送の観点で適切且つ対称性のある発送元と配達先に変更

| 入力 | 出力 | レイヤー | フェーズ | 担当者 | 輸送手段 | 発送元 | 配達先 |
|---|---|---|---|---|---|---|---|
| トランプのカード | トランプの箱 | 家庭の部屋 | 送信 | 子供 | 人 | 子供部屋の床 | 子供部屋 |
| トランプの箱 | 段ボール箱 | 家庭 | 送信 | 親 | 人 | 子供部屋 | 家 |
| 段ボール箱 | コンテナ | 陸運業者 | 送信 | 運転手 | トラック | 家 | 港 |
| コンテナ | 船 | 海運業者 | 送信 | 船員 | 船 | 港 | 海 |
| 船 | コンテナ | 海運業者 | 受信 | 船員 | 船 | 海 | 港 |
| コンテナ | 段ボール箱 | 陸運業者 | 受信 | 運転手 | トラック | 港 | 家 |
| 段ボール箱 | トランプの箱 | 家庭 | 受信 | 親 | 人 | 家 | 子供部屋 |
| トランプの箱 | トランプのカード | 家庭の部屋 | 受信 | 子供 | 人 | 子供部屋 | 子供部屋の床 |

これでかなりキレイに整理できました。

もし、どこか２つのレイヤーを1つにまとめて扱おうとしたらどうなるでしょう。
一気に複雑度が増します。

親が陸運業者の運転手だったとしても、2つのレイヤーを1つにするより、家庭と仕事は分けて考えたほうが複雑度を抑えらるのは明らかです。

いかに単純なことの繰り返しに落とし込むことが大切であるかを表しています。

## まとめ

スケールの大きな物を設計する際に複雑度を抑える考え方を紹介しました。

ポイントはこのあたりです。

- トランプのカードの束のように、扱うものが揃っていると複雑さを感じにくいこと
- 階層ごとに同じことを繰り返すことで巨大な物でも複雑さを感じにくいこと
- それらはカードや階層といった型として同一視できるところがポイントであること
- 表にして情報を整理すると精度を上げられること

慣れてくると「ここは同じことが繰り返されている」とわかるようになるので、大きな物に対する不安も小さくなります。

さらに "同じことが繰り返されている" という前提で整理すると、 "フェーズ" "運転手" "船員" のように、隠れていた存在にも気付けるので、より精度の高いものを作れる自信にもつながります。