Vueコンポーネント設計を検証するためにRealWorldプロジェクトのソースコードを再設計してみた

こんにちは、アドテクノロジーディビジョンの弓場です。
今月の2月2日にVuexの4.0がリリースされ、Vue3を本番で安全に使える準備が整いつつあります。
そのためVue3へのマイグレーション及び新規プロジェクトでの利用を考え始めていて、いろいろな設計について試行錯誤を繰り返しています。

そこで今回はVue3のComposition APIに向けた設計方針を考える過程と結果をご紹介したいと思います。
なお、この記事ではVue3に対しての固まった設計の考えが出てきませんのでご注意ください。

tl;dr ざっくりと伝えたいこと

  • 設計を考える際にはRealWorldプロジェクトの再開発が程よい大きさのコードだった
  • 現段階でわかっている弊社の組織体制に合う設計方針は以下のような感じだった
    • Atomic DesignのAtomsとMoleculesをデザイナー領域として使用してもらう
    • ContainerとPresentationalの考えを使って実装を分離

目指す設計方針

設計は再利用性が属人化などを避けるためにどういう設計にするかを考える機会が多いと思います。
ですがフロント向けであれば有名なMVVMにすれば良いというものでなく、フロントエンドにはAPI通信やデータ加工、整理とUI側のデザインの調整が存在します。

また、自分の部署ではフロントエンド専属という人が存在せず、バックエンドエンジニアとデザイナーが互いにフロント領域に対して開発しています。
そこで問題になってくるのがバックエンドエンジニアの人たちがコーディングした内容をデザイナーの人たちがメンテナンスできなくなるということです。
そういう状況になった場合、よくある手法としてデザイナーの人たちがFigmaなどのデザインツールを使って作成した画面を元にバックエンドエンジニアの人たちが開発するという方法だと思います。

この記事を読んでいる人であれば「普通なやり方」と思われるかもしれません。
ですがこの方法だとちょっとしたズレや色の誤りなどが発生したときの意思疎通をするためのコミュニケーションコストが発生すると思っています。
コミュニケーションは開発速度を下げるボトルネックになりがちです。こういった要素をなるべく少なくして「自分が考える内容を直接サービスにアウトプット出来る」のが理想なはずです。

そこで私はデザイナーの人が画面のレイアウトや画面の動きを実装して、
バックエンドエンジニアがデータ加工やAPI通信のロジックを組み込めればスムーズな開発を実現出来るのではないかと考えました。

TODOアプリやMarkdown編集アプリではフレームワークとは向き合いにくい

理想的な開発を体現するためには沢山の試行錯誤が必要になります。 そういった場合によくTODOアプリなどのシンプルなものが用いられることが多いですが、このようなサービスで開発を行うケースは極稀だと思います。
そのため複雑さに対しての設計やフレームワークの良さ、悪さに気づけません。

そこで私は以下のプロジェクトに対して設計を考察してみることにしました。

RealWorld
https://github.com/gothinkster/realworld
Vueのサンプルページ: https://vue-vuex-realworld.netlify.app/
Vue 3のサンプルページ: https://mutoe.github.io/vue3-realworld-example-app/

RealWorldとは簡易SNSサイトを様々なフレームワークで実装したサンプルコード集が集まったプロジェクト名です。

このプロジェクトは「一覧ページ」「会員登録ページ」「ログインページ」といったWebサイトでよく出てくるページが登場します。
また、フロントエンド開発をする際に欠かせないAPIも用意されており、デモサイトで使用しているAPIに対してリクエストしても問題ありません。
そして、APIが既にあるのでフロントエンドの開発や設計考察に集中出来ます。

再開発してみて気づいた課題と自分が考える方針

まず最初に考えたのはAtomic Designを使ってみるということでした。
Atomic Designはデザイナー向けのフレームワークだと思っており、
「Pages」以外の要素はデザイナーが主導で開発していく領域、「Pages」はバックエンドエンジニアが開発していく領域という風に見ておりました。

f:id:AdwaysEngineerBlog:20210226140902p:plain

しかし、実際にRealWorldでそのように分離してみるとコンポーネントに対してAPIの処理を含めたほうが効率が良さそうに思えるところや
Vueのコンポーネントの親子の階層がより深くなってデータ伝達の複雑さが増して、開発効率が下がっているのではと実装中に思う機会が多々ありました。

f:id:AdwaysEngineerBlog:20210226140919p:plain

そこで自分は上記のようにバックエンドエンジニアが開発する領域を変えてみることにしました。
そうすることによってロジック周りの再利用性やコードのシンプルさなどが向上したと実感出来ました。

そしてOrganismsやTemplatesもデザイナーの人たちが開発に参加しやすくするために以下のツールの導入を考えていました。

  • Templatesをデザイナーが用意出来るようにしたい
  • Storybookを使ったページデザインの管理

そこで自分が目につけたのがReduxのPresentationalとContainerのコンポーネント設計を組み合わせることです。

f:id:AdwaysEngineerBlog:20210226140933p:plain

この設計は大まかにいうとコンポーネントごとに表示とロジックを分ける設計です。
今回は上記のようにOrganismsのコンポーネントを2つに分けて、Storybookの場合はTemplatesからOrganismsを使用するように制御をかけて実現しました

Vue3のComposition APIをどのように扱うか

React界隈ではHooksの登場によりこの設計は主流ではなくなっているようなので(※1)
現在は、Composition APIを組み込んだ際の設計を2パターン考えています。

※1 https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0

パターン1 PageとContainerのコンポーネント間だけComposition APIのStateの送受信を許可する

f:id:AdwaysEngineerBlog:20210226140949p:plain

このパターンではOrganisms用のコンポーネントをそのまま使用しつつ、Organismsで使用しているStateを
Pagesにもデータを共有しやすくなるためコンポーネントのPropsとEmit伝達を余分に増やさなくて済みます。

さらに、バックエンドエンジニアとデザイナーがそれぞれ独立して開発出来る体制を維持出来るため、
コードのコンフリクトで問題が発生する機会が少なくなります

こちらの方式で実際にコーディングをしてみたときに感じたのは、
一人で開発していると設計が少し複雑化に感じることがあることです。

また、デザインの切り替えをしたいときにもorganismsの再利用がやりにくいというところも
問題ではないかと感じいました。

パターン2 PageでOrganismsが使用出来るState等をProvideする

f:id:AdwaysEngineerBlog:20210226141001p:plain

本来のAtomic Designの形を維持しつつ、OrganismsのロジックをComposition APIから取得させることにより
OrganismsにAPI通信の通信結果を渡したい場合やOrganismsのデータをPageで扱い場合にも複雑でデータ通信を使わなくて済みます

こちらの方式で実際にコーディングをしてみたときに感じたのは、
仕組みはシンプルで型定義も機能するため保守性も問題なさそうに思いました。

ですが、デザイナー側がInjectした関数やクラスをOrganismsに適応させるのに少し言語の知識を必要とされるため
そこをどちらが行うかというところが問題になりそうだと思いました

おわりに

RealWorldプロジェクトぐらいの規模のソースコードであれば、
いろいろなパターンを考え実際にコーディングが出来るため、考えているだけでは思いつかないところにも気づくことが出来ます。

様々な設計を思いついたり、界隈の有名な設計を見てプロダクトに適応しようと思う機会は多くあると思いますが、
このように少し複雑なサンプルプロジェクトの再開発などが良いのではないかと思います。