5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

アトミックデザインで初心者が躓きがちな3つのポイント

Posted at

この記事は以下の動画「アトミックデザイン初心者が気をつけるべき3つのポイント」
の台本です。本編と併せてご活用ください。

アトミックデザイン初心者が気をつけるべき3つのポイント


最近、アトミックデザインが
一部のエンジニアに誤解されている気がするんだ。

「UIコンポーネント単位にファイルを細かく分けるだけ」
と考えている人が多い気がするが、
それだけじゃ開発上の意味はほとんど無い。

例えば1万行のコードが書かれたファイルを見つけた時
君はそのファイルを100行ずつ100個のファイルに分けようとは思わないだろう?
何かしら意味のある分け方をするはずだ。

アトミックデザインを「UI単位に分けるだけ」と捉えるのは、
これと同じようなことだ。本質を理解しておかないと、意味がない。

だから今日はアトミックデザインを取り入れるに当たって
初心者が躓きやすいこと、実際俺が実務でどんな風に使っているのか伝えたい。

今日お伝えするのは3つのポイントだ。
・どうやってコンポーネントを分ければ良いのか
・コンポーネントをステートレスに保つ事
・コンポーネントをプレゼンテーショナルとそれ以外に役割分担する事

今挙げた3つのポイントのうち1つでも理解できていなかったら
この動画が役にたつかもしれないぞ。

まずアトミックデザインはデザインの概念で、開発の概念ではない。
デザインを考えるための考え方であり、開発のための考え方ではない。
だからエンジニアがアトミックデザインを採用して開発しようと考えた時、
本来の用途が違うものを使おうとしている事は、認識しておこう。

簡単に言うとアトミックデザインは
「小さなコンポーネントから作り始めて、それらを組み合わせて画面を作り上げる」こと。

例えばこんな画面があったとする。
入力項目、ボタン、説明文、タイトル、ヘッダ。

アトミックデザインのコンポーネントの最小単位はatomだ。直訳で「分子」だ。
ボタンや入力項目だけの小さなファイル、
例えばbutton.vue、input.vueみたいなファイルを作って
atomというディレクトリに格納する

続いてこいつらを組み合わせたファイルを作る。
例えば入力項目とボタンとラベルが3つで1つ、ハッピーセットだとするなら
この3つを束ねたsearchForm.vueみたいなコンポーネントを作って、
moleculeというディレクトリの下に配置する。
formInputの中では、button.vueとかinput.vueをimportして組み込んでいる。

次はヘッダを作ってみよう。
ヘッダにはいくつかの文字と、先ほど作成した検索フォームが含まれている。
こいつらを束ねてheader.vueみたいなコンポーネントを、organismディレクトリの下に作る。

こんな具合に、atomから作り始めて、少しずつmoleculeやorganismに組み込んでいくのが
アトミックデザインの基礎だ。

さて、ここまでは簡単だ。
ここからが実務で直面する問題だ。

君に1つ質問だ。
「どこまでをmoleculeとして扱い、どこからをorganismとして扱う?」
俺が椅子で一回転する間に考えてみよう。

君はこう考えるかもしれない。
「複雑なモノはorganism, シンプルなモノはmolecule」

でもこういう曖昧なルールでチーム開発を始めると何が起きるか?
「これはシンプルだからmoleculeだと思う」
「いいや、これは複雑だからorganismだ!」

みたいな、自分の感覚に基づいた議論が始まる。
こういう議論が起きなよう、もっと判断しやすいルールを考えてみよう。

「それ単体で意味をなすものはorganism」
「単体で存在しても意味をなさないものはmolecule」
みたいな分け方も見たことがある。

でも「単体で意味をなすもの」って言われて、君はピンと来るかい?
例えば検索ボックスは単体で意味をなすだろうか?

「何を検索するかわからないから、意味をなさない」という人もいるだろう。
でも同時に「検索することがわかっているのだから、意味はなしている」と思う人もいるだろう。

仮にここに新たなラベルやサムネイルを追加して
「今すぐ商品を検索しよう!」と説明を加えたら、どうだろう?

何を検索するか分かる以上、このコンポーネントは意味をなしているのだろうか?
逆に、この文字列が短くなって「検索!」だけの文言になったときは
単独で存在しても意味が伝わらないとみなすのか?

こうなってくると、ディレクトリ決めるだけで果てしない議論が発生する。
「どうやって誰でも一瞬で分かるルールでコンポーネントを分けるか」
これがアトミックデザインを採用した初心者が一番ハマりやすいところだと思う。

そこで一つアドバイスだ。無理に分けなくて良い。

アトミックデザインには5つの要素がある。
Atom, molecule, organism, template, page。

でも正直に言って、世の中の大半のプロジェクトは
無理に5つの層に分けるほど大きくないと思う。

無理に5つに分解しようとするから混乱するのであって、使う層は3つでも構わない。
例えばmoleculeは使わない、templateは使わない、と一部を捨てても構わない。

例えば俺が作っているPRANETというサービスではMoleculeは使っていない。
atomを組み合わせる場合はorganism。以上だ。
これはmolecule?organism?みたいな
開発にほとんど関係ない無駄な議論を避けるためだ。

そもそもアトミックデザインはデザインの原則なんだから、
エンジニアの開発設計に100%フィットするはずがない。
アトミックデザインの原則に完璧に従おうとして混乱するぐらいなら
少しぐらい原則を変えて、使いやすいように応用したほうがいい。

大事なポイントその1をおさらいしよう!
「分け方に悩むぐらいなら、無理に分けなくていい」

さて今度は2つ目の大事なポイント、
コンポーネントをステートレスに保つ、について話そう。

ステート、ってのは直訳で「状態」だ。
ステートレス、はつまり「状態がない事」
つまり状態がないコンポーネントを作ろう、
って事を俺は言っている。

例えばさっきのボタンをクリックするたびに
数字が増えるカウンターを追加したとしよう。
これがボタンの下についている。

このサイトでは検索ボタンには必ずカウンターがついてくることになってるから、
「カウンター」みたいなatomを新しく定義して、
検索枠moleculeに含めたとしよう。

初心者がこのコンポーネントを作るとき、
加算した値をmoleculeの中にdataとして持とうと考えるかもしれないが、
これはやめた方がいい。

dataをもつのではなく、
propとして外から受け取るべきだ。
自分自身にdataをもつのではなく、
自分を使う親クラスからデータをもらってこよう。

どうしてこれが大事なのか?
これはテストの書きやすさと、再利用性に大きく関係してくる。

例えばdataを自分自身に持っている
状態のコンポーネントをテストしたいとしよう。
数字が100になっても、何かの間違いでマイナスになっても
デザインが崩れないか確認したい。

数字を100にするためには、
ボタンを100回押さなきゃいけないから大変だ。
それにボタンを押しても数字は増える一方で、マイナスにならない。
そもそもテストが書けないんだ。

一方、親からpropを渡してあげるようにしておけば、
Propに100を渡せばそれで終わる。
-100だって渡せる。
コンポーネントにあらゆる値を簡単に与えて
表示が崩れない事を確認できる。

これがステートレスなコンポーネントの強みだ。

Storybookというツールを使って表示崩れをテストしたり、
スナップショットテストを書く時に
コンポーネントがステートレスになっていれば、楽チンだ。

テストしたいコンポーネントにpropを渡して、
どんな風に表示されるか見るだけだからだ。

テストが非常に大事なことは前回の動画でも解説した通り、
どれだけテストしやすいコードを書くか、っていうのは
中級レベルのエンジニアにとっては大事なポイントだ。

そして何よりステートレスなコンポーネントは再利用しやすい。
ステートレスであれば、
そのコンポーネントには親から渡した値が入るだけだから
どんな状態になっているのか想像しやすい。

こんな理由から、
コンポーネントは基本的にはステートレスに保った方が再利用性は高まるだろう。
特にatomとかmoleculeとか下位層、
つまり小さいコンポーネントが属している層は再利用されることが多いから、
これらの層はステート(状態)を気にするといい。

ただし一点注意が必要だ。
フレームワークによっては
ステートレスコンポーネントだと再描画が頻繁に発生して
パフォーマンスを悪化させる可能性がある。
ちゃんと自分が使うフレームワークを調べてから書くのがいいぞ。

というわけでおさらいだ。
「コンポーネントには状態を持たせず、ステートレスに保つ」

さて最後のポイントだ。
「コンポーネントをプレゼンテーショナルと、それ以外に分ける」

プレゼンテーショナルってのは「見た目専用」みたいな意味だ。
見た目を整える役割だけを担っているコンポーネントの事を
プレゼンテーショナルコンポーネントって呼ぶ。

いくつかプレゼンテーショナル「じゃない」例を挙げてみよう。

例えばさっきの検索枠を例にとってみよう。
ログインしていないユーザが検索ボタンを押したら、
アラートを出す機能を作ったとしよう。

ボタンが押される前にユーザの
ログイン状態を知る必要があるから、
コンポーネントが描画された瞬間にAPIリクエストを飛ばして、
ユーザのログイン状態を取得するようなロジックを
moleculeの検索枠に実装したとしよう。

この時点で検索枠は
プレゼンテーショナルコンポーネントではなくなるし、
再利用性は一気に落ちる。もうほぼ確実に再利用できない。

この検索枠を仮に何も知らないエンジニアが
100個ページに埋め込んだら、
ページが表示された瞬間、
自動的にAPI通信が100回飛んでしまうからだ。
こんなコンポーネントは再利用しづらいよな。

こういう事を避けるために、
再利用したいatomやmoleculeのコンポーネントは
見た目専用にとどめておいて、
ロジックを持たないのをオススメする。

例えば検索ボタンをクリックしたときにはイベントをemitするだけに留めよう。
ボタンはあくまで自分が押されたことだけを知らせる。
その結果何が起きるかは一切知らない。

Moleculeはボタンから受け取ったイベントを
そのままorganismに伝える。

そしてイベントをキャッチした
organismはさらにイベントを受け流してpageに渡す。
pageで初めてリクエストを飛ばすように作れば、
今はすでにリクエスト中だから
次の99リクエストは無視することができる。

Atom、moleculeはデザインを決めるだけ。
なにかアクションがあったときはイベントを飛ばすだけ。
ハンドリングするのはorganism。
外部通信が必要な場合はorganismではハンドリングせず、pageに任せる。

こんな風に分ければ
ロジックはorganism以上に限定されて
Moleculeとatomは見た目専用のコンポーネントであり続けられる。

これは責任の分担、っていう
プログラミングの一番基礎的な原則に則っている。
「どこに何が書いてあるか」わかりやすくなって、
メンテナンスしやすいコードが書けるようになるんだ。
責任の分担について学びたい人は、この動画を見てみるといいかもしれない。

ちなみにうちが作ってるPRANETってサービスの場合、
Vuexみたいなストアとやり取りをするorganismは
「container」ってサフィックスをつけてる。
headerContainerとか、
toastContainerみたいな名前をつけている。
こうすれば「あ、コンテナってついてるから、
Vuexとのデータのやり取りがあるんだな」と一目でわかる。

こんな風に、同じorganismの中でも独自の分け方をして
より細かく責任分担してもいいと思う。
最初は手間がかかると感じるかもしれないが、
後々の改修が非常に楽になるぞ。

さて、おさらいしよう。
「コンポーネントはプレゼンテーショナルとそれ以外に分ける」
「責任分担することでメンテナンスしやすいコードになる」

以上でアトミックデザインを取り入れる上で
初心者が躓きやすいポイントの解説を終了する。役に立ったら嬉しいな。

最後に大事なポイントを全部おさらいするぞ!

その1
無理に5つの層に分けなくていい

その2
コンポーネントをステートレスに保つ事

その3
コンポーネントをプレゼンテーショナルとそれ以外に役割分担する事

アトミックデザインにしろ他の設計パターンにしろ、
エンジニアが気にすべきことに大きな違いはない。

再利用しやすいコードか。
役割分担されているか。
変更に柔軟か。

こういう事を意識しながらアトミックデザインを採用すると
より大きな効果を得られると思うぞ。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?