フロントエンドの中でも、JS書くプログラマと、CSSを書くマークアップと、デザインカンプを作るデザイナで、コンポーネントという概念がズレる。だいたいこれらが一人だったり兼任だったりで1~2レイヤーの開発ステップになるが、完全分業だったり人が多くなると混乱の元になる。
誰かが決定的に間違ってるというつもりはない。正直、どっちかというと本来のデザイナ側の用語定義に倒した方がいい気がしているが、プログラム上の都合もいろいろ混ざってきて、話が簡単ではない。
自分の理解が間違ってる可能性もある。この記事はレビューをもらうために書いている側面もあり、指摘されたら追記していく。
読んだもの。
Atomic Design の大雑把な理解
基本的にはあるコンポーネントは所属するドメインではなく、独立した稼働単位(Atoms)でコンポーネントを分割して、それを積んで(Molecules)、それらを稼働単位(Organisims)でまとめ、ページ(Page)は複数の稼働単位を持つ、という考え方、と理解している。
自分は Reactの src/components はこういう構成を取っていることが多い。
atoms/ Text.js Button.js Comment.js molecules/ CommentList.js organisms/ Header.js UserProfile.js pages/ User.js
コンポーネント名は仮のもの。
ゴールの一致
プログラマ視点だと、コンテキストを作るのが難しいコンポーネントを storybook で作れたり、 storyshots でスナップショットテストできたり、 puppeteer でスクショとったりできて嬉しい。あと地味に嬉しい点で、粒度別にアルファベット順になってエディタで読みやすい。
コンポーネント境界と分割指針がズレる
ここから問題。
デザインとプログラムで、「コンポーネントが独立稼働できる」の単位が違う。
プログラマ的にはコンポーネント定義の再利用性で決めることができる。例えば React のプログラム的に独立した atom を発見するのは簡単で、他の Component を import していない、ということになる。
しかし、デザイン上の出現単位とってプログラム的な依存ステップは必ずしも一致しない。そういうときは、自分はさらにディレクトリを掘って分解している。
atoms/ Button/ ButtonIcon.js ButtonLabel.js index.js
1ファイルに押し込めないのは、コードが長くなって可読性が悪くなったりテストしづらい、というプログラム上の問題で、後は import path を深くしたくないので、 ./atoms/Button
で import できるというのは変えない。index.js から import するだけ。index.js 以外は、「外から見てプライベートなモジュール」としている。
ただ正直 Atoms と Molecules はプログラム上の境界が曖昧でいつも困っている。上の例だと atoms/Icon.js になるのかもしれないが、そう出来ないときもある。
こうして、プログラム上では Atoms が Atoms に依存したり、Molecules が Molucules に依存しそうになって、(さすがに Atom が Molecules に依存することはないが)都度解釈をこねくり回している。
プログラム上にしか存在しないデータパス(props)
プログラマ的には、React Component は基本的に props のデータパスを設計し、末端でデザインが表出する、というマインドセットになる。
逆に、デザイナ的にはデザインモックから起こしていくので、その過程にデータパスは存在しない。プログラマはそのデザインモックを見て、props 渡す経路を考えたり、 connect する単位を考えることになる。
コンポーネント A - B はデザイン上独立しているように見えているのに、A - B のデータパス上の関係においては依存があって、実質的に紐付いている場合、これは Atom か Molecule かの解釈が立場によってズレる。
自分は、基本的には Organisms から redux へ connect にするように心がけるが(これは React のパフォーマンス的な理由もある)、デザイン的な稼働単位に忠実に分割すると、 Atoms に直接 connect せざるを得ないケースも出現する。
Pages はデザインモックなのか
Pages は 「ページ単位のデザインモック」単位と定義されていることが多いが、 実データを入力に持てる JS ではページ単位の入力という単位に pages を当ててることのほうが多いような気がする。自分はとりあえず Router でマウントする単位としている。
この辺や Templates、いろんな実装を見たが人によって解釈が違う。 templetes/
に DefaultLayout.js
みたいな 「ヘッダーとその中身」みたいなコンポーネントがあったのもみた。(流石にこれは語感から拾っただけだと思うが)
React や storybook を使うとき、storybook が実質的にデザインモックの実装になったりするので、 Pages の実装では、モックであるというというのは忘れたほうが良いのではないか。
また、例示した DefaultLayout のような、Layout 情報だけを持っているユーティリティ的なコンポーネントがどこに所属するか難しい。Row や Column などは Atom でいいのか? それに相当する言葉を自分が知らないだけかも。
おわり
とりあえず自分が決めているのは、デザインガイドから作るのではなく、大きな Organisms を作りながら、ユースケース単位で小さなコンポーネントに分割していく、というフローを取っている。最初にデザインガイドをトップダウンでみっちり決めてもあんまり使われない。
正直一人で作るときは完全にプログラマ都合で作るのだが、大規模だとそうもいってはいられなくなる。
間違っていたり、こうしたらいいい、というのを指摘いただけると嬉しいです。