BCD Design によるコンポーネントの分類

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

昨年は時間がとれずアドベントカレンダーに書けなかったのですが、ようやく時間ができたので、書きたくてうずうずしていた最新のコンポーネント分類手法とその周辺のノウハウのお話しを書こうと思います。

記事の最後に具体的な導入事例や、運用で得られた知見も追記されているので、ぜひご参考にしてみてください。

## BCD Design のご紹介

AtomicDesign での悩み事を解消する新しいコンポーネント分類手法 **BCD Design** を紹介します。
AtomicDesign は**設計手法**、BCD Design は**分類手法**です。

![1. BCD Design ロゴ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41058/4652c55c-e7df-0b93-f677-38d657321c91.png)

## BCD Design とは

### 概念軸による分類

**BCD Design** はコンポーネント名に使用される**単語**の**意味**や**性質**を**相対的**に利用することで、 コンポーネントを **Base Case Domain** の**3つの概念**へ**法則的**に配置し、**体系的**に管理できるようにする**分類手法**です。

#### Base - 基礎 (名詞)

**基礎的な機能**(名詞)そのもの、事実上の **“型”** を表す単語のことを指します。


#### Case - 状況 (動詞/形容詞/名詞)

**状況**(動詞/形容詞)や**状態**(名詞)を表す単語のことを指します。


#### Domain - 関心 (名詞)

**人(ロール)**や**物**(名詞)など **"関心の対象"** を表す単語のことを指します。

※ 2021-06-10 以下の表の内容を更新しました
![スクリーンショット 2021-06-10 12.32.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41058/366c48c7-53fe-d9ec-afce-db70850c4ea7.png)

### Common を追加した BCCD 適用(2021-06-10 更新)

Domain 内の単語の抽象度に大きなばらつきが出たり、抽象的、共通の関心が増えてるので、よりキレイに分類する場合は Common を加えた4つに分類します。

#### Common - 共通の関心(名詞)

関心の中で色々な Domain と組み合わせて使えるもの、どの関心でも幅広く共通で利用できるもののことを指します。

詳しくは [BCD Design の Common を徹底解説](https://qiita.com/misuken/items/d51c181487cf611821f4) をご覧ください。

![スクリーンショット 2021-06-10 12.26.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41058/d2c4308c-53d4-cb9c-b0bf-f84271be4fcb.png)

## BCD Design 導入で変わること

### AtomicDesign の悩み事を解決

- molecules と organisms の分類が人それぞれ
- atoms や molecules の分類もまちまち
- atoms molecules organisms のディレクトリ内がごちゃごちゃしてくる

指針が何も無いよりはよいものの、期待しているよりも曖昧でうまくスケールしていかない。

こういった悩み事を解決してくれます。

### BCD Design 導入のメリット

- コンポーネント名の精度が上がる
- コンポーネント名に統一感が出る
- **コンポーネント名から分類を判断できる**
- 粒度分類の質が向上する
  - **organisms が不要になる**
  - **コンポーネント名から粒度を判断できる**
  - 粒度分け不要な場面も増える
- 大量なコンポーネントにもスケールする
- 複雑なコンポーネントのみへ部分的に導入していける

## コンポーネント名

### 大切な4つのパターン

コンポーネント名を突き詰めていくと、主に**4つのパターン**に収束することがわかっています。

次の画像では Icon を例にしていますが、これは Icon に限らず全てのコンポーネントに当てはまります。 [^DDCCBB]
![3. BCD Design 組み合わせ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41058/29d7791e-66f0-61a9-79dd-42cd571fb913.png)

### コンポーネント名はとても重要

開発を繰り返す中で、**コンポーネント名**は思っている以上に**重要**な**存在**であることがわかってきました。

コンポーネントが内部に持つ依存や性質から相対的に名前を表すことがポイントです。
決して**命名するのではなく**、その物の **関心の対象が何** で、 **どういう状況に依存していて** 、 **どういう性質を持っているのか** を見抜くこと、これがものすごく重要です。

### 名前は決めるものではなく決まるもの

名前を**決めるのは主観**で、名前が(依存や性質によって勝手に)**決まるのが写像**です。

**主観**は命名者に起因する**ブラックボックス**であり、時間とともに変化する不定なもので関数的ではありません。
それは他人が同じ前提を持っていないと推測ができず、万人に**非中立で理解しにくい**ことを示しています。

**写像**はコンポーネントの内部に存在する依存や性質に起因して**投影**されたものであり、**関数的**です。
それは他人が推測でき、万人に**中立で理解しやすい**ことを示しています。

**"命名"** するのではなく **"明名"** すると考えるのが良いでしょう。

### コンポーネント名の見直し

コンポーネント名に統一感がない場合、ほとんどは以下のどれかに該当するはずです。
そのようなコンポーネントが見つかったら何がおかしいのかをよく考えてみましょう。

- **関心**に依存しているのにコンポーネント名に表れていない
- 特定の**状況**に依存しているのにコンポーネント名に表れていない
- **型(基礎)**がコンポーネント名に表れていない
- コンポーネント名の単語が **domain case base** の順に並んでない

整理された構造を実現するためには、コンポーネント名が適切であることが不可欠です。

### 関心 = 型 は NG

**User Article Comment** (関心)をそのままコンポーネント名にすると、色々なところで**辻褄が合わなくなる**ので注意が必要です。

辻褄が合わなくなる理由は、**関心の対象**をどのような**型**で表現するのか、その**型**の部分が**未定義**になるためです。

よく考えてみれば、 **UserCard ArticleSection CommentCard** のように暗黙的になっていた**型**が存在していることに気付けます。 [^Articleの名前]

こういった**僅かな綻び**が歪みとなり**全体を崩す**原因になってしまうのです。

## コンポーネント粒度

### organisms は不要

AtomicDesign における **organisms** は厄介な存在です。

- molecules と organisms のどちらにするべきか悩む
- molecules と organisms で循環参照しがち

BCD Design では **organisms は使用しない** ので悩みが減ります。

### 粒度は atoms と molecules で十分

とてもシンプルな法則は次のようなものです。

- **単一的**なものが **atoms**
- **複合的**なものが **molecules**

これなら誰でも理解が容易で循環参照の問題も起こりません。

### 型と粒度の関係性

Base のコンポーネントは常に**特定の粒度に連動**させるようにします。

あのボタンは atoms このボタンは molecules では混乱するので、アイコンと連携しようがコンポーネント名の**末尾**が **Button** で終わるなら常に **atoms** とします。
Image が Anchor でラップされても**主体**としては**画像**に変わりがないため **atoms** です。 [^主体が画像]

あくまで**単語の性質**として**単一的**なものか、**複合的**なものかを判断することが重要です。

| コンポーネント名     | 粒度       |
|:-----------------|-----------|
| Anchor           | atoms     |
| Button           | atoms     |
| Image            | atoms     |
| Dialog           | molecules |
| Form [^Formの粒度]| molecules |
| Menu             | molecules |

atoms を atoms であり続けさせるための工夫は以前記事に書いたので参考にしてみてください。
[AtomicDesign の atom より小さな世界の扉を開く](https://qiita.com/misuken/items/4fe50c5aa9e086e53b2a)

## 軸の転換

### 粒度軸重視から概念軸重視へ

以下は簡単な**ブログサービス**で作成するであろうコンポーネントを**5つの方法**で分類した例です。

1. **粒度軸**で分類しても、**概念軸**の分類をしないとキレイな構造にはならない
2. **概念軸**で分類すると、**粒度軸**で分類しなくても**かなりキレイ**な構造になる
3. **概念軸**と**粒度軸**で分類すると、**非常にバランスの取れた**構造になる
4. **概念軸**と**関心**で分類すると、**スケールに強くなる** [^ディレクトリ省略のヒント]
5. **概念軸**と**関心**と**粒度軸**で分類すると、**スケールに強く**、**関心**をまとめつつ**粒度の恩恵**も得られる [^ディレクトリ省略のヒント]

![4. BCD Design ディレクトリ構造.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41058/096812bf-dde7-76bb-0ced-8be6bb35b59d.png)

### なぜ粒度軸より概念軸なのか

- **粒度軸**の中で**概念軸**の分類を行う
- **概念軸**の中で**粒度軸**の分類を行う

この2つの一番大きな違いは、**関心のまとまり**です。
**DDD** の基本でもあるように、**関心**が**散らばる**ことは**悪手**、**関心の凝集**がとても重要です。

先に**粒度軸**で分類を行うと1つの**関心**が複数のディレクトリに**分散**してしまいますが、**概念軸**で先に分類すると**関心**を**1箇所**にまとめることができるため、**関心**の**理解が容易**で、アプリケーション全体の**複雑さ**を**最小限**に留めることに繋がります。

### 同じ型でまとめるのはアンチパターン

少し話が逸れますが、必ず誰しもがハマる**アンチパターン**が存在するので書いておきます。

**形が似ている**からと言って、**型でまとめる**のは**危険**です。
**forms** のようなディレクトリを作ってアプリケーション内の**全てのフォームをまとめる**ことを想像してみてください。
そのディレクトリ内のコンポーネント名の**先頭**に色々な**関心**の**単語**が並ぶはずです。

**forms** が正解なら **sections lists dialogs buttons** どんどんできるはずです。
それらができる度に**関心**が**散らばって**いき、扱いにくいアプリケーションになってしまいます。

このことは知っていてもついやってしまうほどに、ハマりやすい**アンチパターン**なので気を付けましょう。

## 再帰的な適用

BCD Design は**複雑**なコンポーネントに対して**再帰的**に適用することが可能です。

例えば、あるアプリケーションの中に `XxxTool` という、スコープ内で大量のコンポーネントを持ち、そのスコープ内でのみ扱う**共通部品**があるような場合、構造と依存関係は次のようになります。

**再帰的**に BCD Design を適用することにより、どのスコープでも常に**同じ構造**、**一定のボリューム**で**安定してスケール**することができます。

![5. BCD Design 再帰と依存関係.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41058/a59d3f65-2d37-900e-c5d5-21bd457e33b9.png)

### 依存関係の解説

再帰的な BCD Design の画像の依存関係を簡単に説明すると次のようになります。

1. **case** や **domain** は **base** を参照することができます
2. **domain** は **case** を参照することができます
3. **domain** 内では**主従関係のある兄弟**を参照することができます [^主従関係の参照] [^参照方向]
4. **domain** 内からは、**外界の兄弟**を参照することができます

4 は明示的に **base case domain** から矢印を伸ばしていますが、意味合いとしては以下になります。

- `./domain/*` からは `./base/*` と `./case/*` を参照できます
- `./domain/molecules/*` からは `./domain/atoms/*` を参照できます

ただし、**参照可能な依存関係であっても、 atoms から molecules を参照することはできません。**

**依存関係**は常に**単方向**に保つ必要があります。

### 依存関係は階層を合わせて判断する

先程の図の 4 では `./domain/molecules/xxx-tool/base` 内から、 `./case/*` を参照可能としており、一見問題があるように思うかもしれません。

しかし、これは `./domain/molecules/xxx-tool` のスコープの base で、整理すると `./domain` から `./case` を参照している関係になるため、問題が発生しません。

これと同様に、 3 で `./domain/molecules/xxx-tool` 内の `base` からでも `./domain/molecules/xxx-section` を参照可能な形になっているところも `./domain/molecules/xxx-tool` から主従関係のある `./domain/molecules/xxx-section` を参照していると整理できるので問題が発生しません。 [^toolとsectionの主従関係]

### 高度な依存関係解決法の解説

ここでは依存関係をうまく単方向で解決するノウハウを書きますが、細かい話になるので、依存関係で悩むときに読むことをオススメします。

BCD Design を使うかどうかに関わらず、本質的な部分は同じなので、解決のヒントになるかもしれません。

<details><summary>高度な依存関係の解説を開く</summary><div>

#### 依存関係図の注意点

依存関係図で XxxTool が出来上がるまでの流れと注意が必要なポイントについて解説します。

まずはじめに、 `./domain/molecules/xxx-tool/base/tab-panel` は XxxTool のスコープにおける基礎的な TabPanel です。

この TabPanel はその場で実装するか、 `./base` でライブラリとして提供されている素の TabPanel を import し、 XxxTool におけるスタイルを適用する形になるでしょう。

次に `./domain/molecules/xxx-tool/domain/*` 以下の FooPanel BarPanel BazPanel は `./domain/molecules/xxx-tool/base/tab-panel` の Panel を import して使います。(依存関係図の1)

その FooPanel BarPanel BazPanel を domain の主従関係にある XxxTabPanel が import して組み込みます。(依存関係図の3)

最後に XxxTabPanel を XxxTool が import して、組み込むと、単方向の依存関係を維持した XxxTool が完成します。

多くの人は `./domain/molecules/xxx-tool/base/tab-panel` から FooPanel BarPanel BazPanel を組み込みたくなる局面に遭遇すると躓いてしまいます。

これは BCD Design を使っていなかったとしても、コンポーネントを多く扱うプロジェクトでは起きがちなことです。

`./domain/molecules/xxx-tool/base/*` からは、 `./domain/molecules/xxx-tool/domain/*` を参照できないので、使う側と使われる側を強く意識して、集約する責務のコンポーネントを用意する発想力が必要です。

#### domain 以下の base 内にあるコンポーネント名

もう一つ気にする人が多そうなポイントを解説します。

`./domain/molecules/xxx-tool/base/tab-panel/panel` が、外界の兄弟にあたる `./case` や `./domain/molecules/xxx-section` から何かを import したとしましょう。
その際、コンポーネント名は Panel のままで良いのでしょうか？

答えは「そのままで良い」です。

`./domain/molecules/xxx-tool/base/tab-panel/panel` は XxxTool における基礎的な TabPanel の Panel です。
XxxTool のスコープ(文脈的な意味を含む)においてはそれが標準の Panel であるということから、名前を変える必要はありません。

このあたりはディレクトリ構造を法則的に維持する上でとても重要なポイントになってくるので、スコープがどのように作用するのかもよくよく理解しておく必要があります。

</div></details>

## BCD Design の背景にある思想 

プロジェクトの**要件**を一元管理された **state** と見立てるならば、コンポーネント名やディレクトリ構造は **state** の**写像**です。

**要件**が BCD Design や AtomicDesign という**関数(法則)**を経てコンポーネント名やディレクトリ構造に**描画**されていると捉えることができます。

この `要件 = 構造` という**同期のとれた状態**こそが、万人にとって最もシンプルで**理解しやすい**状態であると言えます。
なぜなら**主観**がなく、**関数的**に推測が可能だからです。

**要件**がコンポーネントの依存や性質に**写像**され、そこからコンポーネント名に**写像**され、そこからディレクトリ構造に**写像**されるのは自然な流れです。
この**一貫性**が、全体の**統一感**、**透明性**、**理解のしやすさ**そのものに直結します。

その上で、コンポーネントの分類に関連するディレクトリ構造は、**減点法**であるとも言えます。
**加点**することはできません。
だからこそコンポーネント名が**重要**なのです。

最初に満点の持ち点があり、**主観**が入る度に関数的に推測できない部分ができて**減点**されていきます。

残った点数がそのディレクトリ構造の分かりやすさの指標になるようなイメージです。

命名すればするほど減点されていく。
それがコンポーネント管理の本質なのかもしれません。

## まとめ

コンポーネント名に使用される単語が持つ、**意味**や**性質**に着目したアプローチで **BCD Design** というコンポーネント分類手法と、その周辺のノウハウや思想を説明しました。

AtomicDesign でやってるけどディレクトリがごちゃごちゃしているという方は、BCD Design で整理したらどんな構造になるかサンプルのツリーを作って試してみてはいかがでしょうか。

**複雑度**が高く、**コンポーネント数が多い**プロジェクトに対しては特に有効に機能するでしょう。

## 補足

- 粒度の管理には AtomicDesign から派生したものを使っても問題ありません
- BCD Design はお好みの粒度管理方法と組み合わせて使うことができます

## 2020-03-28 追記

実際に色々な場面に適用する中で、 BCD よりも Base Case Common Domain の BCCD のほうがよりキレイに分類できる可能性が出てきています。

Common には CategoryList や ErrorList (Case に入れていた名詞)のように、 物の名詞 + 型 が該当し、共通で使える関心(Category や Error)を収めます。

CategoryList は UserCategoryList にも BlogCategoryList にも使用できるように、 Category は User や Blog といった何かの関心と組み合わせて使われる程度に、各具体的な関心と連携可能な共通の関心であると言えます。

## 2020-06-05 追記

実際にプロジェクトで何度か共通の関心に該当するものと遭遇し、 Common に該当するものを common ディレクトリに入れたところ、とてもキレイに収まっています。

CategoryList や ErrorList といった共通の関心に遭遇した場合は迷わず BCCD における common ディレクトリに収めることをおすすめします。


## 2024-12-24 追記

事例や導入に関する関連情報は以下にあります。
具体的な導入事例や、迷ったとき参考になる情報がまとまっています。

- [ニコニコ生放送のBCD Design導入事例](https://qiita.com/misuken/items/09498a496511034627af)
- [ニコニコ生放送でBCD Designを4年運用した知見(導入編)](https://zenn.dev/misuken/articles/93f6f47eb05b94)
- [ニコニコ生放送でBCD Designを4年運用した知見(テクニック編)](https://zenn.dev/misuken/articles/6bfadc96f1fecb)
- [ニコニコ生放送でBCD Designを4年運用した知見(ベストプラクティス編)](https://zenn.dev/misuken/articles/203b9dd3afbf1c)

[^DDCCBB]: 必ずしも1単語ずつの D + C + B ではなく DD + CC + BB ということもあり得ます
[^Articleの名前]: 今回はシンプルにするために端折りましたが本当は Article や Comment は BlogXxx としたほうが良いです。コメントできる対象がブログだけとは限らなくなるのが世の常だからです。 BlogArticleSection BlogCommentCard などは安定感のある名前になります。何かおかしいなと思ったら、何か不足した関係性がないかよく考えてみてください。
[^主体が画像]: 主体がコンポーネント名の接尾辞、 "型" になるので、この例なら AnchorImage になります
[^Formの粒度]: 利用される際は必ず molecules になるため、Form は単体でも molecules として扱います
[^ディレクトリ省略のヒント]: 4 と 5 の場合、必要に応じて接頭辞や粒度のディレクトリを省略すると良いでしょう
[^主従関係の参照]: base や case それぞれの中でも、主従関係に従っていれば参照できます
[^参照方向]: アルファベット順によっては下に向かって参照する場合もあります
[^toolとsectionの主従関係]: この場合は XxxTool が使う側 XxxSection が使われる側という想定です