AtomicDesignを活用する「コンポーネント駆動開発」のススメ
こんにちは!Magic Momentのフロントエンドエンジニアの石田です!
Magic MomentのフロントエンドではReactを採用しており、 コンポーネント設計にはAtomicDesignを採用しています。
みなさんはコンポーネントを使い回せていますか?
今回は僕がMagic Momentで実施しているコンポーネントを使い回すための開発手法をご紹介します。
コンポーネント駆動開発
コンポーネント駆動開発とは、画面を作る際に小さなコンポーネントから作成していき、小さなコンポーネントを組み合わせて画面を作る「ボトムアップ」の開発プロセスになります。
この手法を使用すると、小さなコンポーネントとして再利用できる部品を設計できます。そのため、コードの重複を減らし、保守性を向上させることができます。また、コンポーネントを組み合わせて画面を作成するため、開発の効率性も向上します。
Magic Momentでは、Atomic Designにもとづいてコンポーネントを分割しています。
アトミックデザインとは?
実際にやってみる
サンプルの画面を用意しました。
この画面を実際に分解してみます。
1. とにかくAtomsにわける
見える部分をすべてAtoms
のレベルで見ていきます。
この時に大切なことは、どんなに小さいパーツでも「小さいパーツだからMolecules
やOrganisms
で最初からまとめて作ってしまったほうが楽では?」のように考えないことです。
愚直にすべての細かい部品をAtoms
として分けていきます。
分けることができたら、既存のコンポーネントから作成済みのAtoms
がないかを確認します。
作成済みならそのAtoms
を利用するようにし、新規作成が必要なら新規のAtoms
を作成するようにします。
シンプルに分けることができているので、この時点でProps
を考えることができます。下記は一例です。
- atoms/Logo
- Props
- ※固定のロゴ画像でいけそうなのでpropsはなし
- Props
- atoms/Heading
- Props
- children: React.ReactNode
- level: number
- Props
- atoms/Link
- Props
- children: React.ReactNode
- href: string
- target?: string
- Props
- atoms/InputText
- Props
- placeHolder: string
- defaultValue: string
- onChange: (val: string) ⇒ void
- Props
- atoms/InputSubmit
- Props
- text: string
- onSubmit: () ⇒ void
- Props
- atoms/CoverImage
- Props
- src: string
- alt: string
- Props
- atoms/Text
- Props
- children: React.ReactNode
- Props
- atoms/Button
- Props
- children: React.ReactNode
- href: string
- target?: string
- Props
上記をイメージしながら見た目を実装します。
開発・運用していくと足りないProps
はかならず出てくるので、そうなったときにAtoms
を更新します。
開発を進めていく中で設計が間違っていた場合でも、細かいAtoms
として分かれているので大幅な手戻りは発生しにくくなります。
2. AtomsをMoleculesにまとめる
次にMolecules
にまとめていきます。
Molecules
にするかどうかは、抽象的で使い回せるかで判断していますが、Molecules
として取り扱うべきかについては解釈が分かれるところなのでここでは議論しません。
Molecules
としても分けることができたら、既存のコンポーネントから作成済みのMolecules
がないかを確認します。
ここの時点でAtoms
としてコンポーネントがあるはずなので、Atoms
を利用したMolecules
の実装方針も見えてきます。
- molecules/Menu
- Props
- items: { text: string; href: string; }[]
- Components
- atoms/Link
- Props
- molecules/InputTextSearch
- Props
- onChange: (val: string) ⇒ void
- onSubmit: () ⇒ void
- Components
- atoms/InputText
- atoms/InputSubmit
- Props
- molecules/Card
- Props
- imgUrl: string
- href: string
- title: string
- summary: string
- Components
- atoms/Image
- atoms/Heading
- atoms/Text
- atoms/Button
- Props
この時点ではMolecules
として抽象的なコンポーネントになるので、ロジックは入れないようにして見た目だけを作っていきます。
3. Organismsにまとめる
次にOrganisms
にまとめます。
Organisms
でも使い回せるかを意識してまとめていきますが、セマンティックなコンポーネントとして作成します。
この部分の分け方はサービスにもよりますが、機能別でOrganisms
を分けるとうまくいくケースが多いと思います。
今回の例だと「人気の記事リスト」としてOrganisms
を分けましたが、この他に「新着の記事リスト」も増えていくかと思います。
その場合、「人気の記事リスト」はorganisms/articleList/HotEntry
として作成して、「新着の記事リスト」は organisms/articleList/NewArrival
として作成します。
Organisms
レベルになるとドメインも含まれるようになってくるので、コンポーネントを使い回すというよりも機能を使い回すイメージで作成すると「他の機能に依存されて直しにくい」ということも起こりにくくなり、テストも機能のテストとして分離したほうが作成しやすくなります。
- organisms/Header
- Props
- なし
- Components
- atoms/Logo
- molecules/Menu
- Props
- organisms/Hero/SearchHero
- Props
- imgUrl: string
- title: string
- Components
- atoms/CoverImage
- atoms/Heading
- molecules/InputTextSearch
- Props
- organisms/ArticleList/HotEntry
- Props
- items: { imgUrl: string; href: string; title: string; summary: string; }[]
- Components
- molecules/Card
- Props
とはいえ、ここでも意識したいのは、最初からロジックを入れたコンポーネントとして作らないことです。
開発にはStorybookが便利!
コンポーネント駆動開発には、Storybook
を使用することがオススメです。Storybook
は、コンポーネントのデザインと動作を確認できるツールで、開発の効率性をさらに向上させることができます。
Storybook
Storybook
を使うと、見た目だけのコンポーネントを作るように意識するため、見た目とロジックの責務の分離ができ、開発がしやすくなります。
ロジックはContainer Component
やCustom Hook
に寄せるなどして、コンポーネントと実装を分離することで見た目とロジックのそれぞれのテストのしやすさが向上します。
まとめ
いかがでしたか?
コンポーネント駆動開発をうまく利用すると、コンポーネントの再利用性があがり、見た目とロジックの分離も意識しやすくなります。
コンポーネントの再利用がうまくいってない場合はコンポーネント駆動開発を検討してはいかがでしょうか!
最後に
弊社Magic Momentでは、フロントエンド・バックエンドにかかわらず全方位的にエンジニアを募集中です!Magic Momentに少しでも興味を持っていただけたら是非エントリーください!
8/30にはFindy様主催のイベントにMagic Momentから石田さんが登壇されます!
よろしければぜひご視聴ください!
さらに、こちらのイベントも8/29開催予定です!こちらはオンラインイベントです。Magic Momentの開発がどんなものか興味を持っていただいた方は是非ご参加ください!
Discussion