# プロジェクト概要
Webサイト解析アプリケーションの受託開発を行いました。
イメージとしては、Google Analyticsのようなプロダクトです。解析したいサイト内にScriptタグを挿入し、サイト内のユーザー行動を分析したり広告を挿入したりすることができます。
# チーム構成・規模
10名(自社9名)
### チーム構成
プロジェクトマネージャー
テックリード
インフラ: 1
バックエンド:7
フロントエンド(デザイン・コーディング):1
※バックエンドエンジニアも一部フロントエンドやインフラを手伝っていました。
### チーム内の自身の役割
テックリード・インフラ・バックエンド
# 業務内容
* インフラ(AWS)の設計・構築
* バックエンド開発
* フロントエンド開発
フルスタックエンジニアとして企画・要件定義以外のインフラ・バックエンド・(一部)フロントエンドを担当しています。
特にインフラは自身が社内唯一のAWS SAP(ソリューションアーキテクトプロフェッショナル)保持者として、設計、構築に深く関わっています。
# 実績・取り組み
## インフラ設計とAWS上の環境構築
__前提__
フルスクラッチからの開発案件のため、インフラに使用するクラウドサービスも一から選定する必要がありました。社内のベテランさん達は、オンプレミスのインフラに関する経験は豊富であったものの、クラウドに関する経験はあまりなかったため、AWS SAP保持者の自分が若手でありながらインフラ設計とサービスの選定を任されることになりました。
__課題__
実際にインフラをAWS上に構築するにあたって以下の課題点がありました。
* サービス開始時点でどれほどのアクセスが見込めるのか不透明であったため、高いスケーラビリティを実現する必要があった。
* 実際にアプリケーションを運用するクライアントはAWSの経験が少なく、できる限り保守運用を簡単なものにする必要があった。
* Webサイト分析アプリケーションのため、Webサイト内のユーザーの行動情報(クリックした座標やコンバージョンなど)を保存する必要があったが、データ量が非常に多くなる可能性があった。また、Webサイトへのアクセス数を予測することが難しく、DynamoDBへの保存処理を行うAPIも高いスケーラビリティが求められていた。
* テスト環境、ステージング環境、本番環境でAWSアカウントを分けたため別々のアカウント内で同一の環境を構築する必要があった。コンソール上で3つの異なる環境を手動で設定することは、人的ミスの可能性を高め保守を困難にさせる問題があった。
* 規模の大きいアプリであったため、テストの量も必然と大きくなり自動テストに長い時間がかかることが予測された。毎回手元でテストを走らせ確認することは作業効率の点からあまり現実的ではなく、CI/CDパイプラインを構築する必要があった。
__取り組み__
* アプリケーション(ECS+Fargate)
耐障害性とスケーラビリティを担保し、保守運用を楽にするためEC2ではなくECS+Fargateを選定しました。
* データ集積(API gateway + Lambda + DynamoDB)
Webサイト内のユーザーの行動情報(クリックした座標やコンバージョンなど)を保存するDBは、データ量が非常に多くなる可能性があること、厳密なトランザクション処理は求められないことを鑑みてNoSQLをデータベースに選定しました。DocumentDBかDynamoDBで迷いましたが、スケーリング性能と保守運営の容易さからDynamoDBを選びました。
また、DynamoDBへの保存処理を行うAPIは高いスケーラビリティを実現するためにサーバーレスであるLambdaを導入しました。
* CluodFormationによるインフラのコード化
コンソール上での人的ミスを減らし、異なるAWSアカウント上でも同一の環境をすぐに立ち上げられるようにCloudFormationを使用しインフラをコード化しました。
* エラー発生時の自動通知
Cloudwatch Logsで「Error」の文字列が検知された場合、自動でSlackにメッセージを送るLambda関数を作成しました。
* Github ActionsによるCI/CDパイプライン構築
テスト、リンターを走らせる時間と、ステージング環境へのデプロイを行う手間と時間を削減するため、Github ActionsでCI/CDパイプラインを構築しました。
Github Actionsを選んだ理由は他のCI/CDサービスと比較し、コストが低かったためです。
また、Github Actionsのワークフローファイルの作成も一人で担当し、プッシュ時に自動でRspec, Rubocop, ESlint, Brakemanを走らせ、マージ時にステージング環境、本番環境にデプロイするようにいたしました。
__工夫した点__
* インフラのコード化やECS+Fargateの採用など、環境の立ち上げだけでなくその後の保守運用が楽になるような構成を意識しました。インフラの設定が設定した本人にしかわからないブラックボックスのような状況になるのを避け、仕事を引き継いだ人にも理解できるような状況を目指しました。
* 急激なアクセス数の増加にも対応できるようにスケーラビリティを意識しました。
* 開発者がアプリの開発に集中できるようにするため、できる限り自動化するよう意識しました。
## ヒートマップ機能とチャットボット機能の作成
__前提__
権限管理やバッチ処理等、幅広くバックエンドに関わっていましたが、主にアプリケーションのヒートマップ機能とチャットボット機能を担当いたしました。
* ヒートマップ機能
Webサイトを訪れたユーザーがサイト内でどのような行動を取ったかをヒートマップとして描画する機能です。
* チャットボット機能
Webページに挿入するチャットボットを作成できる機能です。アプリケーション内でチャットボットのデザインやシナリオを設定すると、Webサイトに設定したチャットボットが挿入されます。表示する時間帯や、一度消されたあと再表示するまでの期間なども設定できます。
チャットボットと言ってもAIなどを使ったものではなく、決められた質問と選択肢が提示され、選択肢をクリックしていけば回答が得られる、というタイプのものです。
__課題__
* 適切なサードパーティーツールなどが見つからず、自分でヒートマップをCanvasで描画するためのJavaScriptを作成する必要がありました。
* チャットボットの質問と回答(選択肢)の関係がいわゆるツリー構造となるため、拡張性の高い階層構造をどのように実現するのか、という問題がありました。
__取り組み__
* JavaScriptでのヒートマップ描画
以下の方法でヒートマップを実現しました。
1. スクロール位置やクリック座標をAjaxでLambdaに送信し、DynamoDBに保存する。
2. DynamoDBからデータを取得し、CanvasのcreateLinearGradient()やrect()で白黒のヒートマップを描画する。
3. rgbaのaの値に基づき色をつけていく。aの値が低い時は青色に、高くなれば青を抜き緑を混ぜ、最終的には赤になるように色付けをする。
* チャットボット機能のDB設計
複数のツリー構造を表現する方法(経路列挙、閉包テーブル、入れ子集合、隣接リスト)の中から検討し、保守性の高さから平方テーブルを選択しました。
実装コストを下げるためgemのclosure_treeを使用しました。
__工夫した点__
* ヒートマップを色付けするにあたって、どの値を元にどうやって色つければ綺麗なヒートマップを描けるかを意識しました。
* DBを設計するにあたって、更新する際のコスト、整合性、子孫ノードへのアクセスの難しさを意識しました。