新規案件の設計について, 使用したサービスの紹介

## はじめに

公開が遅くなり申し訳ございません。。(11/31があるものだと勘違いしておりました)
「CYBIRDエンジニア Advent Calendar 2021」1日目担当を担当します、＠ntrvです。
ここ2年ほどは恋愛ゲームのサーバサイド開発をメインに担当していたりします。

## アドベントカレンダー再開にあたって

かつてサイバードでも2014年～2017年までは毎年アドベントカレンダーに参加していました。

しかし次第に一部の人がいくつも記事を書く必要がでてきて
記事を執筆することが大きな負担となってきたことから
近年はアドベントカレンダーに参加できずにおりました。

一方かつてのアドベントカレンダーの記事を見て新たに入社してきた方も増え
「記事を執筆したい！」というメンバーが増えてきたこともあり
それにつれアドベントカレンダーを再開しようという機運が高まってきました。

そして今年から、サイバードの紹介・我々エンジニアのアウトプットのスキルを磨くという観点で
アドベントカレンダーを再開させていただくこととなりました。

## Disclaimer

記載させていただきました内容の正確性については保証できかねます。
なのであくまで参考程度に留めていただけると幸いです。

## 概要

今回新たに立ち上げました新規案件での開発の設計について
どのようなサービスを使用して構成されているかお話しできればなと思います。

新規案件の要件としてはざっくり以下のようになっておりました。

* CtoC型の音声プラットフォーム, [Voicy](https://voicy.jp/)をイメージ
* Twitter認証でログインする
* 月額のサブスクリプションを設定して自分のチャンネルで音声配信が可能(配信者)
    * チャンネルを作成し、チャンネルでの売上を一定割合で受け取ることができる
    * チャンネル開設にあたっての審査が(基本的には)存在しない
* サブスクリプションを購読し、チャンネル登録を行う(視聴者)
* 長時間の音声をアップロードする場合があるので、プログレッシブダウンロードに対応する
* いいね数、再生回数の表示

私は全体の設計・開発スケジュール管理・インフラとフロントエンドの開発を担当しておりました。
サーバーサイドの開発については、別の方1名ないし2名に担当していただいておりました。

## 構成

* 上記の要件と開発側の事情を踏まえ、以下の構成となりました。
* メインシステムと音声変換システムに分離して構築することになりました。

### 共通

* Sentry
    * 各所(フロントエンド・サーバーサイド・Lambda)で発生したエラーを集約・監視し、詳細を分析することができる。
* Terraform
    * 構成が複雑かつ、本番環境・テスト環境でそれぞれ用意する必要がありますのでTerraformを導入している
* GitHub


### メインシステム

![APIシステム.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61315/0284ecee-57f4-7fce-d318-4deff3201791.png)

* Laravel(PHP)
    * PHPであれば社内誰でも使用することができるので採用した
    * DIやMiddleware, Seedingの仕組みが印象的
* Angular + Ionic
    * NestJSとAngularをプライベートで軽く使用していたので、採用した
    * サーバーサイドのエンジニアだったので、**DIの仕組みに親近感があった**
    * デフォルトでTypescriptとなっておりフレームワークとして完成していたので、**ライブラリ選定で迷うことがない**と思った
    * これを機にReactiveX(RxJS)を再度学習しておきたかった
    * [ドキュメント](https://angular.jp/docs)が体系的にまとまっている印象で、いざというときにも対応できる安心感があった
* ECS Fargate
    * EC2の管理も含めて**極力インフラの保守をしない構成**にしたかった
    * Fargateを使用したことがないので経験してみたかった
    * Fargate Spotを利用することで、EC2と比較しても[安く手軽に](https://aws.amazon.com/jp/fargate/pricing/)使用することができた
* Aurora
    * 恋愛ゲーム案件で広く使用しているAuroraを採用
    * RDS for MySQLと比較して、**パラメータチューニングの必要性が比較的少ない**ので採用したということもある
* ElastiCache for Redis
    * セッション管理, いいね数・再生回数を保存
* CloudFront, S3
    * SPAのホスティング
    * プロフィール画像の保存・配信
* CodeBuild, Codepipeline
    * CI/CDに使用している
    * 同じAWSなので**アカウント管理が煩雑にならない**
    * VPCに接続できるので、**CodeBuildからマイグレーションを行うことができる**
    * Angularのビルド・デプロイ・SourceMapをSentryにアップロード
    * Dockerイメージを作成し、マイグレーション(があれば)実行、[作成したイメージをECSにデプロイ](https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/file-reference.html)
    * マイグレーションをCodeBuildで行うために、[VPC接続を有効](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/vpc-support.html)にする
        * プライベートサブネットに接続するので、実質NATゲートウェイは必須
* Stripe
    * 決済サービス CtoCの決済サービス(Stripe Connect)が使用できる
    * 利用するための**審査にかかる時間がなかった**(vs 他の決済サービス)
    * 英語だが**ドキュメントが手厚い**
    * SDKが存在している
* Firebase Authentication
    * Twitterでのソーシャルログインに使用
    * 特にFirestoreを使用していないので、[セッションCookie](https://firebase.google.com/docs/auth/admin/manage-cookies?hl=ja)の仕組みをサーバーサイドに実装する
* Mackerel
    * SaaSのサーバー監視サービス
    * サーバーの各種メトリクスの監視と外形監視
    * AWSインテグレーションからメトリクスを取得
    * 追加のメトリクスや死活監視は踏み台(bastion)にインストールしたエージェントから取得している

### ボイス変換システム

* 音声変換についてはMediaConvertを使用しており、非同期を前提としています。そのため別システムとして切り出すことにいたしました。

![ボイス変換システム.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61315/b21f3c34-2b15-e3bf-c6cc-e3fbb614602d.png)

* Lambda
    * 1. S3への音声ファイルアップロードをトリガーに、MediaConvertのAPIを叩き音声ファイルの変換を開始している
    * 2. 変換完了後にSQSにキューが入りますので、それをトリガーにメインシステム側に変換完了を通知
        * サーバー側のステータスを変更する
    * Angularに合わせてTypescriptを使用、rollupでトランスパイルしている
* S3
    * 変換前と変換後の音声ファイルを保存しておく
* MediaConvert
    * アップロードした音声ファイルのHLS形式への変換で使用している
* Mackerel
    * Lambdaの同時実行数の制限を監視
    * SQSの監視を行い、Lambdaでエラーが発生していないかも見ている


## 開発の振り返り

* とにかく工数見積が甘かった
    * Stripe ConnectやAngular, Media Convert, ECS Fargate等の未使用技術をふんだんに使用していた
    * 見積もり当初から厳しいことが想像できたにも関わらず、**悲観的な見積もり**ができなかった。 
    * 昔先輩からきいた、**"ざっくり見積もった工数を3倍すると実際の工数に近いものになる"**という都市伝説？通りとなってしまった
    * 他案件にも参加していたので、そちらにも迷惑がかかってしまった
    * 各方面の助けを借りてなんとかオンスケで開発することができたが、最終的に**締め切り駆動開発**となってしまった
* ペーペーな自分でも案件の開発に参加することができた
    * インフラ・サーバーサイドしか担当してこなかったが、**未経験の分野であっても参加することができた**
* フルリモートワーク下の開発の苦労
    * 開発期間中に1回目の緊急事態宣言が開始したので、最初のうちはコミュニケーションのとり方に苦労した
* 技術的な反省点
    * 動的なOGP対応
        * Twitter等でボイスを共有するユースケースが多いと考えられるので、要件にはなかったが対応したい ~~既にサービスクローズしていますが~~
            * Lambda@Edgeでmetaタグだけ返す[ダイナミックレンダリング](https://developers.google.com/search/docs/advanced/javascript/dynamic-rendering?hl=ja)を考える


この新規案件の開発を改めて振り返ると
他の会社と比較して、弊社では**未経験の分野であっても大抜擢**してもらえることがあるので
経験がない分、修羅の道を歩むこともありますが
最終的に**多くの経験値を得ることができる**と考えております。


## 続きの話として書きたいこと

上記の内容を踏まえて実際に
苦労した話・失敗した話、注意する部分や"ここにこだわった！"みたいな話も具体的にしたかったのですが
最後力尽き、全体の構成と採用理由等を話すだけで終了してしまいました。。
機会があれば以下の中から**続きの話**をしたいと思います！

* フロントエンド
    * HLSをAngularで再生するために行ったことと失敗したこと(Directiveや動的コンポーネント追加)
    * ngrxを導入して状態管理した話とストアの設計に失敗した話
    * よく使用したrxjsのoperatorと使用例
    * [AsyncPipeの初期値null問題](https://blog.lacolaco.net/2020/02/async-pipe-initial-null-problem/)でやっちまったこと
    * ソースコードの整形・linter周りでやったこと
* バックエンド
    * サーバーサイドでのStripeの実装例(クライアントサイドの例を含めて)
    * DIの使用方法やProviderなどのLaravelの基礎的な話
    * 開発環境の構成(Seeding, Factory周り)
    * Lambdaの実装例
* インフラ
    * Terraform周りで小ネタ
    * 細かい注意点等(SQSのVisibility Timeout, awsvpcモードにdocker-composeをあわせる話, SourceMap周り)
* 上記を構想する上で参考にさせていただいた記事

## 最後に

「CYBIRDエンジニア Advent Calendar 2021」, 明日は@march_fさんの「ラズパイで居留守を極める」です。
ラズパイの有効な利用方法について興味があります！
どのような話をしてくれるのでしょうか？乞うご期待！

また、CYBIRDでは好奇心旺盛なエンジニアを絶賛募集しております。
フルリモートワーク下ではありますが
話を聞くだけでも一度、遊びに来てください。
https://www.cybird.co.jp/recruit/