俺的 Atomic Design (Vue)

## 変更履歴
2021/10/31 ... Templates に関する記載を変更。Pages に関する記述を変更

## はじめに
フロント開発における UI デザイン手法として、Atomic Design が一番耳に入るのではと思うが、その Atomic Design を自分なりにまとめたものをメモとして記事にしようと思います。
Atomic Design については、調べるとたくさんの記事が出てくるため、Atomic Design とはなんなのかについては割愛させていただきます。
また、今回の記事では Vue / Nuxt ならではの　Atomic Design を自分なりにまとめた記事となるので、 React / Angular などではあまり参考にならないかもですが、設計指針的な箇所では参考になるかもなので、是非読んでみてください〜。
また、「こうした方がもっとスッキリして簡潔だよ〜」みたいなコメントなどもどんどん募集しています！

## Atomic Design に触れてみて
現在の職場では Atomic Design を採用し開発を進めているが、自分はその　Atomic Design に業務で触れるのは初でした。
ただ、以下のようなレイヤー・役割があるので、それだけでもわかりやすく開発しやすそうだな〜とイメージしていました。

* Atoms（原子）
    * これ以上分割できない最小のコンポーネント
* Molecules（分子）
    * 複数の Atoms を組み合わせたコンポーネント
* Organisms（有機体）
    * １つの機能を表現するコンポーネント
* Templates（テンプレート）
    * ワイヤーフレーム
* Pages（ページ）
    * ワイヤーフレームに値を流し込むためのコンポーネント

この認識を元にいざコーディングとなると、細かい箇所で「ここどうするの？」みたいなのがたくさん出てきて、調べたり、聞いたりしてようやくスッキリした形となってきたので、その内容をこれから記載していく。

### Atoms（原子）
Atoms は最小のコンポーネント(ボタン・ラベル・各入力項目など)となるがその他の細かい決まり事を、以下のように定義する。

* ビジネス UI とビジネスロジックを持たない
* 異なるプロジェクトや様々な箇所で利用できるようなコンポーネントを作成する
* 基本的な UI としての機能しか持たない

ここの「ビジネス」とは、システムならではの要件・要望のことをさしており、システムならではの UI / ロジックを持つのではなく、本当にシンプルな UI / ロジック機能のみを持つ。
その要件を満たすことにより、次の項目(異なるプロジェクトや様々な箇所で利用できるようなコンポーネントを作成する)を実現できる。
以下のコンポーネントは、簡単なボタンではあるが、上記の項目を満たしたボタンとなる

```vuejs
<template>
    <button type="button" class="a-button" :class="[color, { disabled }]">
        <slot>Button</slot>
    </button>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { ButtonColor } from '@/types/components/01-atoms/general/AButton';

export default defineComponent({
    props: {
        color: {
            type: String as PropType<ButtonColor>,
            default: () => 'default' as ButtonColor
        },
        disabled: {
            type: Boolean,
            default: () => false
        }
    }
});
</script>

<style lang="scss" scoped>
// 割愛
</style>
```

カラーの変更や、disabled 切り替えのロジックに関しては UI の基本的な機能となり、どのプロジェクトでも必要そうなロジックとなる。
システム要件は一切入っておらず、とてもシンプルなコンポーネントとなる。

### Molecules（分子）
Molecules は複数の Atoms を組み合わせたコンポーネントとなるがその他の細かいルールを以下のように定義する。

* ビジネスUIは持つが、ビジネスロジックは持たない
* UIとしての機能を持つ

今回でいうビジネス UI とは、ラベル・入力ボックスの配置などを指し、システム上の要件をのんでいいと考えている。
ただ、ビジネスロジックは持たず、シンプルな UI 機能を実装する。
こちらも上記要件を満たしたコンポーネントを記載する。

```vuejs
<template>
    <fieldset class="m-checkbox-group">
        <template v-for="option in options" :key="option.value">
            <a-checkbox :option="option" v-model="model" @change="onChange" />
        </template>
    </fieldset>
</template>

<script lang="ts">
import { computed, defineComponent, PropType, SetupContext } from 'vue';
import ACheckbox from '@/components/01-atoms/data-entry/ACheckbox.vue';
import { ACheckboxOption, ACheckboxValues } from '@/types/components/01-atoms/data-entry/ACheckbox';

export default defineComponent({
    components: {
        ACheckbox
    },
    props: {
        options: {
            type: Array as PropType<ACheckboxOption[]>,
            default: () => []
        },
        modelValue: {
            type: Array as PropType<ACheckboxValues>,
            default: () => []
        }
    },
    setup(props, { emit }: SetupContext) {
        const methods = {
            onChange: (option: ACheckboxOption, checked: boolean) => {
                emit('change', option, checked, props.options);
            }
        };

        const computedMethods = {
            model: computed({
                get: () => props.modelValue,
                set: (newModelValue) => emit('update:modelValue', newModelValue)
            })
        };

        return {
            ...methods,
            ...computedMethods
        };
    }
});
</script>
```

こちらはチェックボックスグループのコンポーネントとなり、チェックボックスの配置などはこちらのコンポーネントで考慮するが、ビジネスロジックは一切持っていない作りとなっている。

### Organisms（有機体）
こちらは１つの機能(ブロック)を表現するコンポーネントであるが、その他の細かいルールを下記のように定義した。

* ビジネスUIとビジネスロジックを持つ
* UIとしての機能はMolecules以降のレイヤーに任せる（例外もあると思う）
* 一画面のビジネスロジックを担う
* 表示するデータは fetch しない(Templates から受け取る)
* その他のビジネスロジックに関わる API 連携を担う(emit しない)

ここのレイヤーはすごく悩んだ記憶があり、詳細に残せたらと思うw
まず、システムの要件・要望は全てコンポーネントに落とし込む。
ただ、ビジネスロジックでソースが膨れ上がる場合はうまく共通化を実施し、スクリプト部分を外出しする。
エレメント部分で膨れ上がる場合は、 **Molecules に出すか検討し、 Molecules の要件を満たせる場合はコンポーネントとして切り出す**。
スタイル部分は丸ごと import するようにし、スタイルのソース( `<style scoped>` 以外は)は vue ファイルには記述しないようにする。（シンプルに実装できた場合は、外出ししなくてもいいと思っている）
Templates レイヤーでワイヤーフレームを表現するため、 **data fetch は Organisms では実施せずビジネスロジックに関わるもののみ API 連携を実施する(データ登録・更新・削除など)**
ただ、そのビジネスロジックの API 連携は上のレイヤーに　emit しない(１つの機能を表現するというルールを壊すことになるため)
機能(ブロック)の単位に関しては、例えばヘッダ・フッタなどで１つのブロックとして定義する。

このようなルールをもとにコンポーネント作成を実施すると空データの場合（ワイヤーフレーム）を考慮できたり、ビジネスロジックがまとまったりと直感的にコンポーネント作成が実現できると思っている。（また新たな問題・疑問にぶつかるかもだがw）

### Templates（テンプレート）
~~こちらはワイヤーフレームを表現するレイヤーとなるが、個人的に Vue / Nuxt ではいらないと思っている。~~

こちらはここまで作成してきた Organisms のコンポーネントを組み合わせるように作成する。
配置など、レイアウトを意識してコンポーネントを組み合わせる。
ルールとしては下記のように定義した。

* API 連携はしない
* Pages　から流れてくるデータを Organisms に分配する
* 似たような画面にも適応できるようデータは汎用的にされたデータを Organisms に流す(どちらかというと Organisms に規則な気がするがw)

Templates はシンプルにする必要があり、script など(ロジック)は記述しない。
スタイルに対し、layout に関する記述のみとする。

### Pages（ページ）
Pages はそのページを表現するためのコンポーネントとなるが主に Templates にデータを流し込むだけのシンプルな定義となる。
本レイヤーのルールとしては下記のように定義した。

* Templates に fetch data を　props で渡す
* ページに関わるものはこのレイヤーで扱う(query param など)
* Templates コンポーネントを１つ定義する
* ページの大枠で表現するために必要な UI ロジックを持つ

ここでの fetch data とはコンテンツを意味すると個人的には定義している。
そのコンテンツを Templates に流し込むことで、 Atomic Design のデザイン手法を維持している。
Nuxt などでは、 asyncData などを用いて fetch し、算出プロパティ(computed)でデータを props で渡すような記述になるかと思われる。
以下のサンプルは vue での Pages コンポーネントとなる。store に関しては、ちょっと適当に書いているので、そこはご了承くださいw

```vuejs
<template>
    <Suspense>
        <template #default>
            <o-data-list :data="data" />
        </template>
        <template #fallback>
            loading...
        </template>
    </Suspense>
</template>

<script lang="ts">
import { defineComponent, computed } from 'vue';
import ODataList from '@/components/03-organisms/ODataList.vue';
import store from './store';

export default defineComponent({
    components: {
        ODataList
    },
    async setup() {
        await store.dispatch();
        const data = computed(() => store.getters());
        return {
            data
        }
    }
});
</script>
```

## まとめ
ここまでざっくりまとめてみたが、なんか書き足りない感じもするw
今後開発していく上で Atomic Design はまだ課題が出てくると思うが、その時はまた調査したりしていこうと思います。
新たに出た課題に対する調査結果や、コメントいただけたりした場合は、随時更新していくつもりです〜。
こちらの記事で何か参考になれば個人的にすごい嬉しく思います！