はじめに
2024年夏に、株式会社サイバーエージェントさんの「次世代トップエンジニア創出インターンシップ ACE」に参加しました!!
今回はインターンで得た学びや感想をブログにまとめておこうと思います。
概要
このインターンは、学生がチームに分かれて、与えられた仕様に沿ってプロダクトを開発するハッカソン形式のインターンです。
学生1人につき社員さんが1人メンターとしてついてくださり、技術的な相談などをさせていただきました。
日程
9/11 ~ 9/24 の2週間で行われました。
チーム構成
チームはB, Cの2チームで、それぞれ5人ずつの学生で構成されていました。
私のチームでは、5人のうち3人がバックエンド、2人がフロントエンドを担当しました。
プロジェクトテーマ
与えられたお題は「架空の動画配信サービス『あれまぁTV』の番組表を作成する」でした。
番組表には「マイリスト登録」機能があり、マイリストに登録した番組は「マイリスト」ページに表示されるようにします。
番組表という1ページの中に様々な情報が詰まっており、UIも複雑なため、パフォーマンス面での工夫が求められました。
9/11(水)~ 9/12(木)
はじめの2日間は現地で対面で行われました。チームのメンバーは全員が初対面だったため、全体オリエンテーションのあとはまずは自己紹介・談笑をして打ち解けることから始めました。
その後はバックエンドとフロントエンドで分かれて設計・開発を進めていきました。バックエンドは特にDB設計やAPI設計など、実装を始めるまでに決めることが多く、全然実装進んでなくて大丈夫か?という不安もありましたが、急がば廻れの精神で進めていきました。
また、朝会・昼会・夕会を設けてチーム全体で進捗を共有し合う会を設け、全体の進捗を把握すると共に、バックエンドとフロントエンドの間で認識の乖離が生まれないよう注意しました。
9/13(金)~ 9/21(土)
この期間はリモートでの作業となりました。チーム内でのコミュニケーションはSlackを使って行い、口頭で話したいことはMetafileを使って行いました。
はじめの2日間でしっかりチームビルディングをしておいたおかげで、オンラインになっても難なくコミュニケーションが取れたと感じています。
9/22(日)~ 9/24(火)
最後の3日間は対面での作業となりました。最後の大詰めです。私達のチームはバックエンドがMUST要件はすべて完成していたため、メンバー全員でフロントエンドの実装を進めていました。分野を跨いで連携ができたのも、チームビルディングがうまく行った成果だったと感じています。
技術面について
技術選定
使用技術は以下のとおりです。
- APIの通信プロトコル
- REST API
- バックエンドサーバー
- Go + Echo
- DBマイグレーション
- Atlas
スキーマ駆動開発
今回、開発期間が2週間と短く、フロントエンドのために早くAPIを提供する必要がありました。
そこで、API周りとDB周りについては、自動生成ツールを使ってスキーマ駆動開発を行いました。
API周り:oapi-codegen
DB周り:gorm_gen
これにより、バックエンドの実装工数を大幅に削減することができました。
APIスキーマの記述については、TypeSpecを使って記述しました。通常のyamlによるスキーマ記述は、保守コストが高くなりがちですが、TypeSpecを使うことで、TypeScriptのような記法でスキーマを記述することができ、保守性が向上しました。
アーキテクチャ
アーキテクチャは以下のようなレイヤードアーキテクチャを採用しました。
- domain層
- usecase層
- infrastructure層←自動生成を多用
- presentation層←自動生成を多用
これにより、各層を疎結合にし、影響範囲を少なくすることができました。レイヤードアーキテクチャでは、似たようなモデルを各層で定義しなければならず、実装コストが高くなるというデメリットがありますが、自動生成を使うことで、このデメリットを解消することができました。
パフォーマンスについて
番組表は1つのページに以下の情報が詰まっています。
- チャンネル名
- 番組名
- 番組がいつ放送されるのか
はじめはこれらの情報を別々のエンドポイントに分けて取得することも考えましたが、これでは1ページの表示に複数回のリクエストが必要になり、パフォーマンスが悪くなってしまいます。
そのため、パフォーマンスを優先して、これら3つの情報を1つのエンドポイントで取得することにしました。
日時の指定をクエリパラメータでするか否かについては、キャッシュヒットレートを上げるために、日時の指定はせずに2週間分の番組データをすべて返却することにしました。
当然データが大きくなってしまいますが、フロントエンドにNext.jsのApp Routerを採用していたため、通信はBackend Server・Frontend Server間でしか行われず、ユーザーの通信速度に依存しないため、問題ないと判断しました。
問題になるのはDBへの負荷です。これに対しては、DBのクエリ結果をキャッシュすることで対処しました。
スケーラビリティ
ユーザー数が増えてもスケールできるようなDB設計を心がけました。
DBのスケーラビリティを考える上で、番組表データとマイリストデータでデータの性質が異なるという点に着目しました。
- 番組表データは、全ユーザー共通のデータ
- キャッシュがしやすい
- readが多い
- マイリストデータはユーザーに応じてデータが異なる
- writeが多い
- キャッシュが難しい
これらを踏まえて、番組表データはレプリケーションとキャッシュを活用し、マイリストデータはシャーディングを活用することで、スケーラビリティを確保しました。
反省点
- ペイロードを圧縮するべき(gzipなど)
- DBへのクエリでcontextの伝播ができてなかった
- gormに依存したコードがusecase層に漏れてしまっていた(エラーハンドリング周り)
- フロントエンドに責務が寄りすぎた
- フロントエンドサーバーでデータを加工するのに時間がかかり、そこがボトルネックになってしまった
- 経験の差から、メンバー間で技術選定に対する理解の深さに差が生じてしまった
- もっと丁寧に情報共有すべき
まとめ
今回のインターンを通じて、スキーマ駆動開発やレイヤードアーキテクチャ、パフォーマンスチューニングなど、実務で使える技術を学ぶことができました。
また、学生同士でチームを組んでわいわい開発をできてとても楽しかったです。チームビルディングがうまくいったおかげで、技術的な議論もしやすく、より良いプロダクトを作ることができました。
株式会社サイバーエージェントではACEを含め様々なインターンシップを開催しているようなので、興味のある方はぜひ参加してみてください!