# プロジェクト概要(個人開発)
技術などの書籍のレビュー・レーティングサービス
各書籍にレビューと8項目について数値評価したものを投稿してもらう。これを一覧検索でき、閲覧できる。
https://github.com/KitamuraTaishi/bookChart-archive (該当リポジトリのアーカイブです)
https://bookchart.jp/
## 目的・背景
技術書などを読む際にその内容が自分に合っている本なのか、より深い情報が得られる手段が欲しかったことが背景になります
## 規模感・チーム構成・担当した役割
- 期間:7ヶ月(現在も開発進行中)
- 人数:1名
- 担当した役割:企画、設計、フロントエンド、バックエンド、インフラ、テストなど
## 使用技術・開発環境
- Frontend:
- Next.js (Typescript,Tanstack Query)
- GraphQL
- Tailwind CSS
- GA4/GTM
- など
- BFF
- NestJS(Typescript)
- など
- Backend:
- Go (Gin)
- Gorm, sqlite, postgreSQL
- TDD(repositoryのbook-service)
- など
- Others:
- Docker / Docker Compose
- JWT 認証
- k3s / argocd / cert-mnamager / sealedsecrets / Treafik / Klipper
- cloudflare(R2/CDN/NS)
- Github Actions/Github Packages
- tailscale
- bigquery(GA4)
- linode(VPS)
- など
---
# 取り組んだ課題
## 課題の内容
CI/CDとデプロイのサイクルの簡易化とコードの責務の分離・GA4・BIgqueryの導入・インフラコスト・集約結果の抽出の負荷低減・フロントエンドのキャッシュ戦略
## 技術的なアプローチ・工夫点
- CI/CDとデプロイのサイクルの簡易化
- CIはGithub Actionsを用いることで、pull requestと merge mainの自動テスト化(TDDによるユニットテスト、統合テスト)
- git tagによる分離ビルド、リリースができる。(4つのサービスで存在するがモノレポで管理しながら、git tagによって特定のディレクトリだけを分離ビルドできる)
- k8s環境でのargoCDによるGit Opsの実現(GithubをSOTとして管理。コンテナのversionはbook-v0.0.3などのタグがつけられているのでこれをマニフェストに適応するが、開発中は基本的にlatestタグで行った。しかし丁寧な運用もできるように構築している)
- コードの責務の分離
- GinではDIコンテナが存在しないので、手組みでDIを行い責務の分離行った。
- Handler: リクエスト受信・Usecase呼び出し
- Usecase: 複数ドメインをまたぐユースケース処理・トランザクション制御
- Service: DTO変換、ドメイン単位での業務ロジック(DB1テーブル相当)
- Repository: GORMを用いた永続化処理の抽象化
- 特にrespository層ではDBの実装ごとのエラーに対応できるように、DBErrorClassifierのDIが行える実装にした
- GA4・Bigqueryの導入
- GTMを用いてGA4とBigqueryの導入を行い、毎日Bigqeuryにデータが格納される。
- 今後の開発運用でデータ分析ようにダッシュボードなどを作成できるようにした。
- インフラコスト
- インフラにはCDの観点でk8sを利用することを決めていたが、EKS(aws),GKE(gcp)などはコントロールプレーンだけでも月に1万円かかり、ここにLBやマネージドDBを導入すると個人では長期運用するの荷重すぎるコストになるので、linode VPS上にk3s(軽量k8s)で構築しLBはKlipperなどk3sのデフォルトのLBで代用し、DBはStatefullSetをもちいることにした。今後cronJobによるバックアップ処理の実装が必要
- 画像配信・CDN活用に関してはR2(Cloudflare)を用いた。「データ転送(インターネット向けアウトバウンド)料金が “無料(ゼロ料金)」なので、画像は配信が多い本書籍サービスにおいては有効でほとんど無料で利用できる。
- 集約結果の抽出の負荷低減
- bookテーブルとreviewテーブルがあり、書籍の一覧などを取得する際にマージが多く発生してしまうことや、ソート種類などで処理が複雑化してしまうことを避けるために集約テーブルを用意した。この集約テーブルの更新は、ユーザーがレビューを投稿すると非同期でレビューの集約計算と書籍情報のマージを行いデータを更新する。レビュー作成とともに同期的に新たな集約計算をしないのでレスポンスが早い。
確実に更新するためにはキューイングシステムになどでイベント駆動で整合性を確認したうえで更新したほうが良いと思ったが、とりあえず開発の速度を優先しfire-and-forget形式を採用している
長期的にはOpenSearchなどの検索エンジンを導入する必要がある。またRedisなどで代用し、インメモリ特性から高速にレスポンスを返すようにしても良かった。
- フロントエンドのキャッシュ戦略
- 書籍レーティングサービスでリクエスト数を抑えるためにTanstack Queryを導入し、検索クエリ、ソートなどをキャッシュキーにしてある程度の時間が立つまではキャシュを利用するようにした。また、レビュー作成時など新しい情報に更新したい場合はキャッシュクリアする動作を追加してある
---
# 取り組みの成果
- CI/CDの整備によって、4サービス(frontend,bff,book-service,user-service)のデプロイがgit tagのみで行えるので、デプロイの手間が少なくなった。
- モノレポではあるがそれぞれのtag(book-v0.0.3など)でビルドするため、コンテナのサイズも小さくすることができた。
- ユニットテストの整備によって、CI時に変更による影響が可視化されやすくなった。
- 責務の分離により各層を薄く保つことに成功し、修正や改修・デバックが非常にやりやすくなった。
- GA4の導入によってユーザーの行動をある程度リアルタイムで見れるようになった
- 個人開発をやっていく上でデプロイコスト(時間や手間)、運用費用(月24ドル程度)を低く抑えて、モチベーションを高くすすめられるようにした
- キャッシュをうまく活用することで無駄なリクエストを減らすことができたが、APQなどでキャッシュ階層を変えることを検討しても良かった。