大規模トラフィックに耐えるVue.jsのサーバーレスアーキテクチャについて

## アジェンダ

---

- 自己紹介
- サーバーレスアーキテクチャとは（ざっくりと）
- 今回のサービスについて（ざっくりと）
- 今回の開発環境について
- 通常のSPA開発との相違点
- 良かった点
- 悪かった点
- まとめ

---

## 自己紹介
![profile.png](https://qiita-image-store.s3.amazonaws.com/0/197260/9e43081d-5bf9-67b4-7228-02154a1498b1.png)　秋津 辰弥（あきつ たつや）
フロントエンドエンジニア / [株式会社GIG](https://giginc.co.jp/)
2016年1月 ~ 2017年10月　株式会社NAKED
2017年11月 ~ 現在　株式会社GIG

Twitter : [@Rote_libelle](https://twitter.com/Rote_libelle)
note    : [あっき〜](https://note.mu/tombo_code)

---

## サーバーレスアーキテクチャとは（ざっくりと）

---

* サーバーでの処理を極力最小限に抑えて、必要な時に必要なだけインフラを利用する
* インフラの管理・運用を気にしなくていいので、その他のタスクに集中することができる

つまり、今回のサービスはリクエストが最大どれくらい来るのか、メモリはどう制御するのか、実際に運用が始まったら想定していたより多くリクエストが来てしまった時どう対応するのかというインフラ側の諸々のタスクを考えなくていい構成にしました。
これのおかげで基本的にインフラは気にせずに、実際にコンテンツ制作に工数を注力することが可能になります。

---

## 今回のサービスについて（ざっくりと）

---

今回のサービス開発では、前提として、トラフィックに波がある想定で設計しています。
また諸事情によりherokuをサーバーとして使用せざるを得ない案件だったため、このトラフィックに耐えうる為に一部サーバレスのアーキテクトを採用しています。

---

GCSに、ButterCMSから登録された各コンテンツ情報を毎晩深夜にバッチ処理を走らせ、JSONファイルとして保存しています。
ButterCMSからは以下のコンテンツ情報が登録されていきます。

* 各都道府県 × レコメンド × 日本語・英語コンテンツ情報
* おすすめ情報
* カテゴリ別イベント情報

![json.jpg](https://qiita-image-store.s3.amazonaws.com/0/197260/b71b5792-5f9d-1219-7cfb-3e37dc89172f.jpeg)

---

## 今回の開発環境について

---

* heroku
今回は諸事情によりherokuをサーバーとして一部使用しました。
Add-onsでButterCMSもここに登録しています。

* ButterCMS
SaaSの一つで、ヘッドレスCMSと呼ばれています。
端的に言うと、コンテンツを表示する機能を持たず、APIでコンテンツの配信を行うCMSです。
今回はこのButterCMSにコンテンツを登録していきました。

* Google Cloud Service
JSONファイルの保存場所として利用しています

* Akamai
大量アクセスに対してサーバーダウンを避ける為、一度登録されたJSONファイルや
CSS、JSファイル類はAkamaiにキャッシュさせて各ブラウザに表示させていきます。

---

今回のVuex構成は以下の通りです。
基本的にはVuexの構成に準じたものを採用しました。

* actions
サーバーからAPIを叩いて情報を取得する処理を記述しています。

* mutations
サーバーから取得したデータをstateに保存したりVueファイルで表示する為に整形処理を行います。
ビジネスロジックもここに記述していきます。

* getters
Vueファイルからstateの情報を取得する処理を行います。
ビジネスロジックとは違い表示に必要な処理を記述していきます。

* state
APIやJSONから取得した情報はstateに保存していきます。
今回はTypeScriptを採用していない為、ここに記載されている型形式がそれぞれの値の格納するべき型定義の役割を担っています。

![env.jpg](https://qiita-image-store.s3.amazonaws.com/0/197260/7ab30c7c-2f51-3614-bc6d-aa43037cdecc.jpeg)

---

## 通常のSPA開発との相違点

---

###### APIの接続先が混在する

今回リアルタイムに更新が反映されて欲しい情報はherokuにget / postリクエストを送信しています。
ただ、全ての情報をherokuで管理してしまうと今回想定しているトラフィックの負荷にheroku側が耐えきれない可能性が大きく、
サーバーダウンに繋がりかねない為、バッチ処理でGCSにJSONファイルとして情報を格納し、
フロント側からそこを見に行く仕組みにしました。

---

###### フロント側でソートや配列の再形成等の処理を多くしている

あらゆるコンテンツ情報や、登録後変更がかからないユーザー情報はバッチ処理でJSONファイルにしている為、データ同士を突き合わせて様々な条件式のもと抽出するロジック周りはフロント側で処理をしています。
予めバックエンド側で処理が完了しているDBを見に行くのと違うためそこの処理実装が多く入っています。

---

###### 変数名をわかりやすくする

action-typesや、envに記述しているAPIのURLを登録した変数名など、GCSからJSONを取得してきて表示するコンテンツは種類も量も多いため、どのactionがどの情報を取得しているのか一意に理解できるよう気を遣いました。

---

###### ユーザーアクションによって更新された情報をpostする回数が少ない

今回はサービス登録時にユーザーが登録した情報を元にGCS上のJSONファイルを見に行く処理が基本になってくるので、VuexからVueファイルへの処理、Vueファイル側での表示処理までの見通しが良くなりました。

---

## 良かった点

---

###### DBの知識がなくてもファイルの有無や中身の状況を把握できる

GCS上のどこに何のJSONファイルが格納されているという情報だけあれば、そこを見に行くだけで済むのでSequelなどの用意やDB構造の理解する工数などがなくても状況を確認することができました。
また、仮にButter上やバッチ側の改修作業が同時進行で入っていたとしても、ローカルにJSONファイルを用意することでフロント側の開発への支障も最小限に抑えられたと思います。

---

###### バックエンド開発環境を立ち上げる必要がない

通常DB構成等もあればDockerを立ち上げたりしてバックエンドの開発環境も立ち上げる必要があるため、Macのメモリーを多く消費してしまったり、フロント側の開発環境の立ち上げコマンドやホストとの重複などあれこれに気を遣う必要がありますが、既に登録されているJSONファイルを見に行って実装を行うためその手間が省けました。

---

###### api通信がシンプル

ログイン状態やQueryパラメータなどを送ることで、ユーザーそれぞれのコンテンツ情報等を取得するというが通常のAPI通信ではあるあるだと思いますが、JSONファイル名に各地域名や言語を割り当ててアクセスするだけで取得できるため、actionでのひとつひとつの処理が見通しがよく実装が行えました。

---

## 悪かった点

---

###### GCS上にJSONファイルがないとエラーになる

登録後変更がかからないユーザー情報の一部はJSONファイルとしてGCSに登録されていますが、開発段階で全体に関わるJSONファイルが突如なくなるとページ全体がエラーになるなどの弊害があり、根幹部分のロジック構築時などはローカルにJSONファイルを用意する工数がかかりました。

---

###### アクセスポイントが大量になる

コンテンツそれぞれに合わせてアクセスするJSONファイルが異なる為、機能やページが増えるごとにアクセスポイントが増えてしまい管理に気を遣う必要が出てきました。
類似名なども出てきかねないため、必要以上に変数名を意識する必要があります。

---

###### ビジネスロジックが多くなる

GCS上に登録されているユーザー情報などに基づいて更にコンテンツを出し分ける必要がある場合など、複数のJSONファイルを取得し配列を再形成するなどのロジック処理をそれぞれのmutations内で記載する必要がある為、ロジックの管理と処理が冗長化しないよう気を遣う必要がありました。

---

## まとめ

---

* インフラ側の管理工数がないので、フロントに注力することが出来たことは大きいです
    * JSONファイルで管理している為、開発時は最悪ローカルで用意することも可能です。
* DB確認ツール含め環境構築も楽でした
    * フロントだけで良いので、npm等々フロント用のものがあれば十分開発着手できます。
* API通信が楽でした
    * それぞれのAPIが別れているため、同じ画面の状態変化時の処理も追いやすいです