GoとNuxt.jsでServerlessアプリケーションを構築する（AWS SAM+Serverless Framework）

**GoとNuxt.jsを使って、Serverlessな日記アプリケーションを作りました。**
※2020年5月14日追記: アプリケーションを削除しました。

https://pastory.me

ソースコードはGitHubに置いています。
- [GitHub - hareku/pastory-web](https://github.com/hareku/pastory-web)
- [GitHub - hareku/pastory-api](https://github.com/hareku/pastory-api)

用いた技術は以下の通りです。

- Nuxt.js（フロント）
  - Serverless Framework
  - Lambda + API Gateway + S3 + CloudFront
- Golang（バックエンド）
  - AWS SAM
  - Lambda + API Gateway
  - DynamoDB
  - Firebase Authentication

Serverlessアプリケーションを開発しての感想をまとめていきます。

## 「Serverless Framework」vs「AWS SAM」
両者ともServerlessアプリケーションのための開発/デプロイツールです。

今回はフロントである**Nuxt.jsはServerless Framework**を使い、バックエンドの**GolangはSAM**（Serverless Application Model）を使っています。それぞれの特徴と感想を述べておきます。

### Serverless Frameworkの特徴
https://github.com/serverless/serverless/

- 開発期間が長く、機能もSAMより豊富で安定している
- 英語で検索すれば大体のことは出てくる（そして解決できる）
- ローカルサーバーの起動はNode.jsしかできない（serverless-offlineというnpmパッケージ）

Node.jsのServerless開発ならこのServerless Framework一択です。**プラグインが豊富**にあり、S3への静的ファイル自動アップロード（serverless-s3-sync）や、Lambdaのコールドスタート対策（serverless-plugin-warmup）も簡単に導入できます。

ただ一方でNode.js以外だとローカル開発ができず、もししようとしても、リクエストを受け付けるコード部分を本番と開発で切り替える必要があります。そういったローカル開発という点では後述するAWS SAMが優れています。

### AWS SAM（Serverless Application Model）
https://github.com/awslabs/serverless-application-model/

- AWSが公式で提供しているため安定して運用できる
- Goを含めたほとんどの言語でローカルサーバーを起動できる
- Serverless Frameworkの後発のため機能が所々足りない

AWS SAMを使えばほとんどの言語でローカルサーバーを起動できます。しかし一つのHTTPリクエストで**毎回Dockerのイメージを立ち上げて処理**するため、レスポンスが最低でも2~3秒かかります。お世辞にもDeveloper Experienseが良いとは言えません。

そしてGolangなどの場合は予めファイルをビルドしておかなければならないため、**Goファイルを編集する度にビルドしなければならない**という手間もあります。またWindowsでデプロイするとLambdaでPermissionエラーが出る点も対応されていません。自分で別途[aws/aws-lambda-go](https://github.com/aws/aws-lambda-go)をリリースフローに組み込む必要があります。自分はおとなしくMacBookからデプロイしました。
[awslabs/aws-sam-cli](https://github.com/awslabs/aws-sam-cli)自体まだベータ版であるため、細かい改善点がまだまだ多いです。**メジャー版でどれだけ改善されるかが期待**されます。

## 認証はFirebase Authentication
独自の認証基盤を作る場合、多くは車輪の再発明になります。Firebase Authを利用するか、もしくはフレームワークに用意されているサードパーティパッケージを利用しましょう。
Nuxt.jsとGoを使ったFirebase Authの導入はこちらの記事が参考になります。
[「Vue.js + Go言語 + Firebase 」で始める! Frontend Backend API 両方で認証するセキュアなSPA開発ハンズオン! - Qiita](https://qiita.com/po3rin/items/d3e016d01162e9d9de80)

## データベースの選択

### DynamoDB
AWSでのServerlessアプリケーションでは基本的にDynamoDBを使うことになります。**DynamoDBはトランザクションにも対応しており、そして簡単にスケーリング**できます。
しかしNoSQLであることは認識しなければなりません。DynamoDBではリレーションを用いてクエリを発行するなどは出来ず、MySQLでは3つのテーブルに対して1つのクエリですむ処理でも、DynamoDBでは少なくとも各テーブルごとに1クエリを発行する必要があります。アプリケーションの要件に合わせ、**DynamoDBで対応できるか、そして今後対応していけそうか**を考慮しておきましょう。

またDynamoDBは公式のDockerイメージが用意されており、ローカル開発に使用することができます。
https://hub.docker.com/r/amazon/dynamodb-local/

### Aurora Serverless
[Amazon Aurora ServerlessでHTTPSエンドポイントができ本当にサーバーレスアーキテクチャで利用可能になる！ ｜ DevelopersIO](https://dev.classmethod.jp/cloud/aws/amazon-aurora-serverless-avaible-http-endpoint/)

最近Amazon Aurora ServerlessがHTTPを使って操作できるようになったみたいですが、**まだまだServerlessアプリケーションに用いるべきでは無い**と思います。
そもそもHTTPで使えて何が嬉しいかというと、VPC外からでもセキュアにAuroraを操作できるようになり、またLambdaにおけるコネクションプーリングの相性の悪さを解消できるという点です。ただ一方でAurora Serverlessはトランザクションに対応しておらず、今後対応するかも明言されていません。
また基本的にAurora Serverlessとのやり取りはJSONであるため、Aurora Serverlessに対応したORMを作るなどして対応する必要があります。その辺りで実装の手間が増えてしまう点は少し懸念点です。
**Aurora Serverlessはあくまで「Aurora provided in serverless（Serverlessで提供されたAurora）」であり、「Aurora for serverless（serverlessのためのAurora）」ではない**のです。

### Firebase Realtime Database、Firestore
DynamoDBと比べて、Realtime Database（以下RTDB）やFirestoreが優れている点はリアルタイム同期ができることです。リアルタイム性を簡単に実現できるので、チャットアプリなどでも採用できます。

- [【v2対応しました】Nuxt.jsとFirebaseを組み合わせて爆速でWebアプリケーションを構築する - Qiita](https://qiita.com/potato4d/items/cfddeb8732fec63cb29c)
- [Nuxt.js, Firebase, Stripe をフル活用してサーバーレスでJAMStackなECサイトを2日で開発した話 - Qiita](https://qiita.com/potato4d/items/13050d5934ff592dc539)
- [ZOZO大忘年会でFirebaseを使った1,000人規模のリアルタイムアンケートを費用2円で制作した話 - Qiita](https://qiita.com/tomohito_takahashi/items/fea4a931ad1734585d1c)

正直Firebaseはまともに触ったことがないで、RTDBやFirestoreについて詳しく述べることはできないです。ただ調べて見る限り、DynamoDBより扱えるデータ型が豊富だったり、無期限の無料枠でも十分に運用できたり、といった点が優れていると思われます。FirebaseがRTDBの次世代として使われていくみたいなので、またFirestoreを使って何か開発したいですね。

- [Firebase Realtime DBを実践投入するにあたって考えたこと - Qiita](https://qiita.com/1amageek/items/64bf85ec2cf1613cf507)
- [Cloud Firestoreを実践投入するにあたって考えたこと - Qiita](https://qiita.com/1amageek/items/d606dcee9fbcf21eeec6)

## 明確な理由を持ってServerlessに
今回初めてServerlessアプリケーションを本格的に作ってみました。
一言でまとめるなら「明確な理由が無いかぎりServerlessにするべきではない」という結論です。

今回の場合は**サーバーコストを最小限にしたい**という理由があり、そしてDynamoDBで十分に表現できるモデルという要件を満たしていました。
Serverlessだから開発コストを下げられるというのは、半分正解で、半分間違いです。扱う技術や要件によってはServerlessではない方が良いパターンもあります。

### 参考サイト
- [GitHub - bxcodec/go-clean-arch: Go (Golang) Clean Architecture based on Reading Uncle Bob's Clean Architecture](https://github.com/bxcodec/go-clean-arch)
- [Nuxt.js on AWS Lambda with Serverless Framework - mya-ake com](https://mya-ake.com/posts/nuxtjs-on-aws-lambda/)
- [Amazon DynamoDB まとめ - Qiita](https://qiita.com/kenichi_nakamura/items/bc60d4702b5f88d59cfb)
- [DynamoDB ベストプラクティス - Qiita](https://qiita.com/inouet/items/bcf9467a65b27c362ecf)
- [AWS SAM + DynamoDB Local + Go で始めるサーバレスアプリケーション開発 - エムスリーテックブログ](https://www.m3tech.blog/entry/advent-calendar-2018-6)

### メモ：
Lambdaにおけるコールドスタート対策は現在行なっていないので、サーバーの応答に数秒かかる時があると思います。
こんな感じで、まだまだブラッシュアップすべき点は残ってます。

もしこのサービスを気に入ってくれたのならば、GitHubへのコントリビュートをお待ちしています。

- https://pastory.me
- https://github.com/hareku/pastory-web
- https://github.com/hareku/pastory-api