個人開発・スタートアップで採用すべき最強のアーキテクチャを考えた

# 結論
![aws.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234396/1ef837ac-7d97-e04c-4e73-04df56ed1a34.png)

「アジリティ」「コスト最適化」「スモールな構成」「開発スピード」という観点でWebアプリケーションのアーキテクチャを考えてみました。

* ServerlessFrameworkを使い倒す
* フロントエンドはS3 hosting + CloudFrontで。SSRもLambda@Edgeでできます
* データベースはRDSは使わずにDynamoDBで
* APIは基本的にGraphQL。必要に応じてRESTも簡単に追加できるよ。

**補足(2022/04/12)**
最近個人開発しているこちらのWebサービスはこのアーキテクチャに沿って作られています。
このアーキテクチャでどんなものができるのよ、という視点で見ていただくと面白いかもしれません。
↓

https://englister.yunomy.com

# 背景 アーキテクチャに絶対の正解はない
アーキテクチャには絶対の正解はありません。
なぜなら、プロダクトやフェーズによって**求められる要件**が異なり、それに適したアーキテクチャを考える必要があるからです。

例えば、先日障害で証券取引が1日休場になった東証の高速取引システムは、**安定性や信頼の要件**に全振りをしていて、400台のサーバで負荷分散をしていたり業務系と運用系でネットワークを分けて障害の影響範囲を局所化するようなアーキテクチャになっています。
AWSやメルカリなどの成長企業では**拡張性の要件**が高く、イノベーションを起こし続けるために全体のアプリケーションに影響を与えずに新しいサービスを展開できるように、マイクロサービスアーキテクチャが採用されます。

では個人開発やスタートアップはどんな要件が重視されるか考えてみます。
[こちら](https://www.idaten.vc/post/%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%88%E3%82%A2%E3%83%83%E3%83%97%E3%81%8C%E5%A4%B1%E6%95%97%E3%81%99%E3%82%8B%E7%90%86%E7%94%B1%E3%83%88%E3%83%83%E3%83%9720)にスタートアップが失敗する理由のランキングが掲載されています。

***スタートアップが失敗する理由ランキング***
１位 市場ニーズがなかった
２位 資金の枯渇
３位 適切でないチーム構成
４位 競争力で負けた
５位 使いにくいプロダクト

これらはアーキテクチャで全て解決できる問題ではないですが、どんな要件が必要かが見えてきます。

１位 市場ニーズがなかった → 顧客の声に合わせて仕様を変化させていく**アジリティ**
２位 資金の枯渇 → **コスト最適化**
３位 適切でないチーム構成 → なるべく専門家を必要としない**スモールな構成**
４位 競争力で負けた → **開発スピード**を高めて競合を抜き去る
５位 使いにくいプロダクト → 改善サイクルを高める**アジリティ**

個人開発/スタートアップに必要な要件は**「アジリティ」「コスト最適化」「スモールな構成」「開発スピード」**ではないかという仮説が立ちます。
この観点で技術要素を検討してみました。

# 技術要素
背景にて、個人開発とスタートアップには**「アジリティ」「コスト最適化」「スモールな構成」「開発スピード」**が優れている技術要素を採用すべきだという検討を行いました。
この観点で私が優れていると思う技術要素を紹介していこうと思います。
構成は「フロントエンド」「データベース」「バックエンド(API)」の３段階で説明していきます。

## フロントエンド
### フロントエンドフレームワークの選定
フロントエンドフレームワークはReact,Vue,Angularの３大フレームがあります。
**「開発スピード」「アジリティ」**という観点で考えると、**TypeScript**で型付けをすることが必須だと思います。入力補完の恩恵を受けつつ、どんなデータを受け渡せば良いかが一眼でわかることで開発効率をあげつつ、仕様変更でコードを変更した際のエラーチェックがコンパイル時にわかるためです。
そしてTypeScriptとの相性という観点では**React**が他の２つのフレームワークに比べて群を抜いているという印象で、フロントエンドフレームワークは**Reactを採用すべき**だと思っています。
また、**「開発速度」「スモールな構成」**という観点で、私は素のReactを使うよりも**Next.jsを使うことを推奨**します。というのもNext.jsは、必要だが実装するのは面倒なことを**No Config**で実現できるためです。具体的にいうとルーティングとSSR(SEO, SNSのOGPのため)です。
以上より、**Next.js(TypeScript)をフロントエンドフレームワークとして推奨**します。

### フロントエンドのデプロイ先
Next.jsのデプロイ先として真っ先に頭に浮かぶのは、vercel, Netlify, Herokuなどがあります。
これらは非常に楽にデプロイができて、基本無料で使えるため選択肢に入ってきます。
しかし無料枠には**必ず機能的な制限**があるため、スケールすることを前提にするならばできればAWSで構築したいところです。
**S3 hosting + CloudFront**であればAWS無料枠で使えますし、無料枠がなくなっても**ほぼ無料**です(S3は0.025USD/GB, CloudFrontは0.114 USD/1GB)。
vercel等のサービスで有料アカウントになると固定費がかかってきますが、AWSのこの構成であればonDemandな課金になるので、**「コスト最適」**という点で**S3hosting + CloudFrontを推奨します**。
ちなみにvercelの無料枠もほぼ無制限みたいなものなので、極端なスケールを前提としないのであれば**vercelも全然ありです**。
フロントエンドのデプロイ先としてEC2やECSなどのサーバを使うことは**「開発速度」「コスト最適化」という観点から論外**かなと思っています。

Next.jsのSSRはLambda@Edgeで行います。
この構成のデプロイは**ServerlessFramework**を使うことで、No Configで簡単に行えます。
https://github.com/serverless-nextjs/serverless-next.js

## データベース
### RDS vs DynamoDB
結論から言うと**問答無用でDynamoDBを選択することを推奨**します。
正確にいうとDynamoDBでは表現しきれないデータ構造や複雑すぎるアクセスパターンの場合はRDSにせざるをえないのですが、初期のフェーズではDynamoDBで作って無理が出てきたらRDSとの併用を考えるという形が良いと思います。
**「コストの最適化」**という観点からいうと、Dynamoは従量課金ができるのに対してRDSはインスタンスの起動時間で課金されます。またRDS+LambdaはRDS Proxyの導入を行わないといけません。 Aurora Serverlessもありますが、こちらは**コールドスタートが致命的**です。
**「アジリティ」**という観点からいうと、DynamoはNoSQLなのでスキーマが固定されず仕様の変更に柔軟に対応できますが、RDBはテーブル間の依存関係があるためテーブルを捨てにくくマイグレーションやSQLの作り直しが発生します。
**「開発速度」**という観点では、Dynamoはデータベース機能の管理が不要であることとGraphQLと相性が良いことで開発効率をあげることができます。

### DynamoDBの設計
DynamoDBを推奨しますが、**RDBライクにDynamoDBを設計してしまうと確実に詰みます**。DynamoDBはJoinができないため、Joinが必要のないようにテーブル設計をする必要があります。
RDBとNoSQLはテーブル設計のやり方が全く異なるので、ここに必ず最初学習コストをかけてください。

お勧めのリンクを貼っておきます。
[サーバーレスアプリケーション向きのDB設計ベストプラクティス](https://pages.awscloud.com/rs/112-TZM-766/images/20190905_%E3%82%A4%E3%83%81%E3%81%8B%E3%82%89%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B%E3%82%B5%E3%83%BC%E3%83%8F%E3%82%99%E3%83%BC%E3%83%AC%E3%82%B9%E3%82%A2%E3%83%95%E3%82%9A%E3%83%AA%E9%96%8B%E7%99%BA-%E3%82%B5%E3%83%BC%E3%83%8F%E3%82%99%E3%83%BC%E3%83%AC%E3%82%B9%E3%82%A2%E3%83%95%E3%82%9A%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E5%90%91%E3%81%8D%E3%81%AEDB%20%E8%A8%AD%E8%A8%88%E3%83%98%E3%82%99%E3%82%B9%E3%83%88%E3%83%95%E3%82%9A%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B9.pdf)
[Amazon DynamoDBのデータモデリング](https://dev.classmethod.jp/articles/reinvent-2019-report-cmy304-for-developer/)
[DynamoDB に合わせた NoSQL 設計](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-general-nosql-design.html)

## バックエンド(API)
### REST vs GraphQL
**「アジリティ」「スモールな構成」「開発スピード」**という観点で**GraphQLを推奨します**。
GraphQLは今まで持っていたバックエンドの責務をフロントエンドに移譲する側面があります。それによって、UIに変更が加えられるたびにAPI修正をする必要がなくなり**アジリティが高まります**。
またLambdaでREST APIを記述すると、エンドポイントの数だけLambdaを作ることになる(そうしない方法もあるが)が、GraphQLは**単一エンドポイントなので「スモールな構成」を維持できます**。
GraphQLはフロントエンドから利用する時に非常に扱いやすく、これは体感ですがRESTよりも**フロントエンドの開発スピードは高くなっている気がします**。

とはいえ、RESTの方が向いている処理もあります。例えばファイルアップロードなどはGraphQLでやろうとするとひと工夫が必要です。そういう場合はRESTのエンドポイントを併用することは全く問題ありません。**API Gateway**を間におくことで同じドメインで対応が可能です。

### デプロイについて
API Gateway + Lambda(GraphQL) + Lambda(REST) + DynamoDB の構成のデプロイはServerlessFrameworkを使えば簡単に可能です。
[こちら](https://github.com/serverless/examples)に各言語のテンプレートが用意されているので探してデプロイまでやってみてください。その手軽さに驚くと思います。
ちなみに自分は[こちらのテンプレート](https://github.com/serverless/serverless-graphql)をカスタマイズして使いやすいようにしてから使っています。

### 認証認可について
認証認可はログイン機能などを実装する際は**Cognito**を活用します。開発スピードを意識すると自前実装しちゃった方が早い時もあったりしますが、セキュリティに関わるのでマネージドサービスを使った方がアンパイでしょう。
こちらもServerlessFrameworkで追加可能です。

## 最終形
![aws.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234396/7bad9e5b-c9f7-0ee7-c0fb-2e7e48957902.png)

以上をまとめると上の図のような構成になりました。

# まとめ
「アジリティ」「コスト最適化」「スモールな構成」「開発スピード」という観点でWebアプリケーションのアーキテクチャを考えてみました。

* ServerlessFrameworkを使い倒す
* フロントエンドはS3 hosting + CloudFrontで。SSRもLambda@Edgeでできます
* データベースはRDSは使わずにDynamoDBで
* APIは基本的にGraphQL。必要に応じてRESTも簡単に追加できるよ。

がポイントだと思います。
個人開発/スタートアップに関わる方々の参考になればと思います。
もし興味がある人がいれば自分のテンプレートも公開しようかなと思います。
ありがとうございました。

# 追記 DynamoDBについて
DynamoDBをメインのDBにするところに非常に批判が殺到しているので少し追記します。
言いたいことは３つあります。
## １、RDSのインスタンスコストは従量課金できないこと
全くアクセスがなくても月2000円~くらいかかってしまうのが惜しいところで、ほぼ無料で始められるサーバレスの１番のコストペインポイントだと思っています。個人開発だとここだけでDynamoを採用する大きな理由になると思います。事業としてやるならこれくらいのコストは誤差、というのは理解できます。

## ２、DynamoDBは思っている以上に表現力が豊か
「Dynamoは制約が多いから採用は慎重にすべき」は部分的に正しいと思います。ただ、DynamoDBの設計のプラクティスがあって、例えばAdjecent Patternを使えばMany to Manyの関係でも全く問題なくアクセスすることが可能です。検索条件が複雑でGSIを使い切るとか複合キーが複雑になるなど特殊な状況でない限りRDSを渇望するような事態にはならないと思っていて、十分にDynamoDBのポテンシャルを活かせていない可能性が高いと思っています。

## ３、DynamoDBは最初にサクサク開発したいから
記事に書いているようにDynamoDB採用の主な理由はアジリティ・開発速度・コストです。
プロダクトマーケットフィットしていないプロダクトは後の技術的負債を心配することよりも、小さく素早く失敗できるようにしておくことを重視すべきと思っています。

## とは言ったもののRDBもいいよね
本心を言うとRDB+ORMで十分に開発速度とアジリティは出ると思っていて、インスタンスコストをケチりたかったと言うのが本心かもしれません。なのでDynamoの部分をRDS Proxy + RDSに置き換えるのは全く藪さかではありません

## その他の意見について
「firebase, Netlify, Heroku, Amplifyでいいんじゃないか」説は、そうかもです。
ServerlessFrameworkとAWSを使うことのメリットは、「プロバイダ側の制約がない(無料枠だとコールドスタートがあるとか、アクセスレートがあるとかがない)」ことと「リソースに対して自由度が高い(CloudFormationで書けるので)ので発展性がある」というところかなと思います。