Twitterが𝕏になってしまったので、自分でTwitterみたいなSNSを作ってみた（その１）

# 作ってみた
百聞は一見に如かずということで、スクリーンショットをいくつかぺたり。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160980/70a294c3-2711-22e7-10cd-bf728f0a4d1b.png" width="480">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160980/cac72a7f-d328-f8cd-8323-f9b1e881368c.png" width="480">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160980/67ea238c-b2b1-d8ed-a23c-ed6bc099b02b.png" width="480">

もちろんスマホでも見れるよ

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160980/2920ac32-2c17-c7d0-c88d-61a854a89e70.png" width="480">

ちなみに筆者のツイハイアカウントは [@nr_ck](https://www.twi-high.com/profile/nr_ck) です。

# どうやって作ったかまとめてみた
このエントリは個人で作成したサービスの「[ツイハイ！](https://www.twi-high.com)」について、インフラからバックエンド、フロントエンドまで一気通貫でどんな設計で動いてるかまとめたものになります。

[ツイハイ！](https://www.twi-high.com)は現在のところアーリーアクセス版というステータスで、かつ実験的なプロジェクトという建付けとしています。まだ基本的な機能のみのため、10年前くらいのTwitterを思い出すかもしれません。小さいSNSで、機能もユーザー数も少ないサービスですが、その裏側はスケールしやすいように考えた設計があります。この設計についてフロントエンド設計、[バックエンド設計（次のエントリ）](https://qiita.com/nr_ck/items/8731b0436cbfa7528846)、 **インフラ設計（このエントリ）** という3つの視点でQiitaに残したいと思います。
これらのエントリを通して、SNSを提供するために必要なサービスやAPIの構成などを感じ取ってもらえると幸いです。

:::note info
本エントリは「[Azure完全に理解した！～ツイハイ！の設計で学ぶSNSに必要なエンジニアリング～](https://booth.pm/ja/items/4963518)」からの抜粋になります。
:::

# インフラ設計
ツイハイはAzure上に構築しています。ツイハイのバックエンド、フロントエンド共にC#で書いているため、.NET系と相性の良いAzureを選択しています。
ツイハイはバックエンドでREST APIを提供しており、これをフロントエンド側から叩く構成にしています。そのため、APIの処理を実行するインフラに **[Azure Function](https://azure.microsoft.com/ja-jp/products/functions)** を使用しています。FunctionはAPIの役割ごとに別々のリソースを作成したため、この複数のFunctionを束ねるため **[API Management](https://azure.microsoft.com/ja-jp/products/api-management)** を使用しています。

システムのデータの保存先に **[Cosmos DB](https://azure.microsoft.com/ja-jp/products/cosmos-db)** を採用し、画像などの静的リソースは **[Azure Blob storage](https://azure.microsoft.com/ja-jp/products/storage/blobs)** を使用しています。フロントエンドのホスティングには **[App Service](https://azure.microsoft.com/ja-jp/products/app-service)** ではなく、 **[Static Web Apps](https://azure.microsoft.com/ja-jp/products/app-service/static)** を使用しています。
この他にもリソース間をプライベートネットワークで接続するため **[Azure Virtual Network](https://azure.microsoft.com/ja-jp/products/virtual-network)** を使用しています。

それではそれぞれ詳細に見ていきましょう。

## REST APIを支えるリソースたち
### Azure Function

**[Azure Function](https://azure.microsoft.com/ja-jp/products/functions)** は、 **[API Management](https://azure.microsoft.com/ja-jp/products/api-management)** からルーティングされたフロントエンドからのリクエストを実際に処理するプログラムがデプロイされます。
ツイハイにはユーザが投稿したツイートを処理する「Tweet API」、ユーザのタイムラインのデータをレスポンスする「Timeline API」、ユーザのプロフィールの編集やユーザのツイート一覧をレスポンスする「TwihighUser API」、フォロー／リムーブの処理をする「Follow API」の4つのAPIが存在します。
その4つのAPIをそれぞれ別のAzure Function上にデプロイし稼働させています。
この場合、それぞれのリソースへアクセスするためのURLは `https://resource-name.azurewebsites.net/api/endpoint-name` になります。それぞれのエンドポイントを叩けば、期待するレスポンスを得ることができますが、APIのエンドポイントごとにFQDNが変わってしまうのは、少々ダサいですね。
そんなときは次のAPI Managementを使用すると良いでしょう。

### API Management

API ManagementはフロントエンドとFunctionの間に立ち、リクエストに応じたAzure Functionにルーティングするために使用しています。ただ、ルーティングするだけでは宝の持ち腐れですので、 **[ポリシー機能](https://learn.microsoft.com/ja-jp/azure/api-management/api-management-policies)** を使い、各エンドポイントへのリクエストに対して一括で **CORS** の制御を行っています。また、Function側ではIP制限やFunction keyなどを使用し、API Management側からのリクエストのみを受け付けるような設定をします。こうすることでFunctionへの直接のアクセスを排除し、API Management経由のみのリクエストを処理するようします。

## データの保管を支えるリソースたち

### Cosmos DB

Cosmos DBは、ツイートやタイムラインの情報を保存するデータベースとして使用しています。サーバーレスプランで構築し、データベース操作で使用される要求ユニット（RU）とデータの保存容量に対して課金されるようにしています。これはまだユーザが少なく、アクセス数が少ないためです。ゆくゆくはプランの変更を検討したいと思います。

NoSQL APIを使用しているため通常のRDBMSとは異なり、非リレーショナルなJSON形式のドキュメントで保存されます。データ間にリレーションが無いため、データの読み出し方、マスタの持ち方を考慮したデータ設計が重要になってきます。このデータ設計さえうまく言ってしまえば、 **「10ms未満の書き込み／読み取り待機時間」** と **「99.999%の可用性」** を得られます。
タイムラインの取得はデータ取得処理速度と可用性がとても重要になってきます（読み込みが遅かったり、サービスダウンして使えないSNSはちょっと悲しいですもんね）。

なお、Cosmos DBとAzure FunctionはAzure Virtual Networkで接続されており、Azureの内部ネットワーク内で通信しています。このため、パブリックアクセスはできず、ネットワーク面でのセキュリティの担保をとっています。

### Azure Blob storage

Azure Blob storageはAzure Functionの実行ファイルのホストやアカウントアイコンなどの公開ファイルの保存に使用しています。公開ファイルなどを保存するパブリックなリソースと、実行ファイルを保存するプライベートなリソースを分けています。

プライベートなリソースはプライベートエンドポイントを設定し、Azure FunctionとAzure Virtual Networkdで通信しています。プライベートエンドポイントはコンテナ、キュー、テーブルなどそれぞれに対して設定ができるため、使用するサービスに応じてプライベートエンドポイントを設定します。正しく設定しない場合、Functionからアクセスができない状態になります。

## フロントエンドを支えるリソースたち

### Static Web Apps

Static Web Appsは、ユーザが使用するクライアントをホスティングするために使用されています。
**クライアントはC#で書いていますが、Blazor Webassemblyで作成しているため.NETの実行環境の無いStatic Web Appsでホスティングすることが可能です。** また、WebAssemblyかつPWAで構成しているため、初回アクセス以降は基本的にキャッシュが使用されます。そのためほとんどアクセスされることはありません。このリソースにはカスタムドメインを設定しているため、独自のドメイン（ www.twi-high.com ）からアクセスすることができます。
クライアントの更新や機能追加があった場合、GithubActionを使用し自動でデプロイが行われるようになっています。

### Azure DNS

Azure DNSはドメイン名の解決に使用しています。XServerドメインで取得した `twi-high.com` ドメインの管理をしています。Static Web AppsとAPI ManagementのデフォルトのFQDNに対しCNAMEレコードを登録しています。
他のDNSサービスを使用しても良いですが、カスタムドメインの設定をする場合など他のリソースとの連携が楽になるため、Azure DNSの使用をおすすめします。

## まとめ

今回紹介したリソース以外にもKeyVaultやManaged IDなど様々なリソースを相互の連携させツイハイ！を構築しています。
インフラ設計部分はここまでです。引き続き、別のエントリで「 **[バックエンド設計（次のエントリ）](https://qiita.com/nr_ck/items/8731b0436cbfa7528846)** 」「フロントエンド設計」について書いていきたいと思います。より詳細な内容は **コミックマーケット102で頒布予定の「Azure完全に理解した！　～ツイハイ！の設計で学ぶSNSに必要なエンジニアリング～」** を読んでいただけると幸いです。

## ご紹介（PR）
きじのしっぽはコミックマーケット102に参加します！　**8/13は西2ホールの「お37a」で会いましょう！**
よろしくお願いします🙇
- 頒布情報
  - [[BOOTH] Azure完全に理解した！～ツイハイ！の設計で学ぶSNSに必要なエンジニアリング～](https://k-s.booth.pm/items/4963518)
  - [[C102当日] 2023/08/13 東京ビッグサイト 西2ホール「お37a」きじのしっぽ](https://webcatalog.circle.ms/Circle/17305442)

コミックマーケット開催当日以外に、BOOTHでも頒布していますので、ぜひご覧ください！

👇下の表紙が目印です👇
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160980/bf22823b-1524-adfe-29f6-a7e2249d5f20.png" width="260px">