0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

10日で作る Go Basic API

0
Last updated at Posted at 2025-11-14

「とりあえず動く API」を超えて、10日でどこまで“ちゃんとした”バックエンドを作れるのか。

このエントリでは、リンク集サービス Tsudzuri のバックエンド API を約10日間で作り上げたプロセスを、アーキテクチャ、ツール、CI/CD、Google Cloud、そして GitHub Copilot(AI エージェント) の活用という観点から振り返ります。

「Go で Web API を作ってみたい」「趣味開発でも設計と運用まで含めてきちんとやってみたい」という人向けに、コンパクトに全体像を掴めることを目指しています。


サービス概要と目標設定

Tsudzuri とは?

Tsudzuri は、複数人で共同編集できる リンク集(綴り) を作るためのサービスです。

  • 匿名ゲストで開始可能
  • 「綴り (Page)」を作成してリンクを追加・編集・並べ替え
  • 招待コード/招待リンクで共有し、参加者全員が閲覧・編集可能

今回の記事では、フロントではなく バックエンド API サーバー(Go) にフォーカスします。

ゴール設定

ゴールは以下に絞り込みました。

  • 最低限必要な Page / User のユースケース を一通り実装
  • gRPC + gRPC-Gateway ベースで、HTTP と gRPC の両方から叩ける API
  • PostgreSQL + ent でドメインを壊さない永続化
  • OpenTelemetry + GCP を前提にしたトレーシング/ロギングの仕組み
  • ローカル開発は Docker Compose + Makefile でワンコマンド起動
  • GitHub Actions で CI(Lint/Test)と CD(Cloud Run デプロイ)までつなぐ

「あとから雑に作り直さなくていい」最小構成を狙い、設計と運用の“ライン”をはじめから決めて進めたのがポイントでした。


アーキテクチャ概要

全体構成

リポジトリ構成は、クリーンアーキテクチャをベースにしたレイヤード構成です。

cmd/api        # エントリポイント (main, DI, OTEL)
config/        # 設定・環境変数
api/           # protobuf 定義 & 生成物
  ├── protobuf/
  └── tsudzuri/
domain/        # ドメインモデル, リポジトリIF
  ├── page/
  └── user/
usecase/       # ユースケース (アプリケーションサービス)
  ├── page/
  └── user/
infrastructure/# DB, Firebase などの実装
  ├── api/
  └── db/
presentation/  # gRPC ハンドラ, エラーコード
  ├── grpc/
  └── errcode/
pkg/           # 共通ライブラリ (log, trace, cache, uuid…)
resources/     # DBマイグレーション

レイヤごとに責務をはっきり分離しています。

  • presentation: gRPC / HTTP の入り口。リクエスト/レスポンス変換とエラーマッピング
  • usecase: ユースケース単位のアプリケーションサービス
  • domain: ビジネスルールとエンティティ、リポジトリインターフェース
  • infrastructure: DB や外部サービス(Postgres, Firebase)の実装

Page / User ドメインの切り方

ドメインは Page と User に絞りました。

  • domain/page

    • Page エンティティ(タイトル、リンクのリスト、並び順など)
    • Link エンティティ(URL, タイトル, メモ…)
    • PageRepository インターフェース
  • domain/user

    • User エンティティ(ID, DisplayName, アイコンなど)
    • UserRepository インターフェース

これに対して、

  • usecase/page に Page 作成・編集・一覧・参加(join)・リンク操作など
  • usecase/user に User 作成・ログイン・取得など

のユースケースを配置し、さらに presentation/grpc/page, presentation/grpc/user でハンドラを実装しています。

ポイント:

  • ent のスキーマはインフラ層に閉じ込め、domain からは見えないようにする
  • ハンドラは usecase のインターフェースだけを知る
  • domain は application / infrastructure の詳細を一切知らない

これにより、「とりあえず動く」ではなく、「拡張しやすい」構成にできました。


開発フローとツール群

ローカル開発は Makefile + Docker Compose に寄せています。

  • make dev : air でホットリロード開発
  • make up / make down : API / DB / Swagger UI の起動・停止
  • make test : テスト用 DB コンテナを立ち上げてから go test ./...
  • make lint / make fmt : golangci-lint と gofumpt、および buf format
  • make generate : go generate + buf generate で ent / protobuf 周りをまとめて生成

テスト実行時は docker compose で Postgres を自動起動し、pg_isready で待ち合わせてから go test を叩くようにしているので、テスト前の手作業がほとんど要らないのがポイントです。

DB は ent + PostgreSQL で、スキーマは infrastructure/db/ent、マイグレーション SQL は resources/migration/ にまとめています。


Google Cloud 上での運用設計

ここからは、「個人開発でも現実的に払えるコスト」 を意識しながら設計した GCP 周りを少し踏み込んで紹介します。

Cloud Run — コンテナ実行基盤の中心

この API は Cloud Run で動かすことを前提に設計しました。

  • 完全マネージドで OS・パッチ・スケーリングを丸投げできる
  • コンテナさえ作ればそのままデプロイできる
  • 個人開発でも Free Tier + 従量課金で現実的なコスト

構成としては、Artifact Registry に push したイメージを Cloud Run サービス(tsudzuri など)で起動する、ごく標準的な形です。

個人利用なので、

  • 最小インスタンス数 0(アイドル時コストゼロ、cold start は割り切る)
  • 最大インスタンス数・同時実行数は控えめ
  • CPU はリクエスト処理中のみ割り当て

といった「とりあえず安く動かす」設定からスタートしています。

Cloud SQL — 共通コアでできるだけ安い構成を目指す

DB は Cloud SQL for PostgreSQL を採用しています。ここでもコストをかなり意識しました。

  • 1 プロジェクトに 「共通コア」的な Cloud SQL インスタンス を 1 つだけ作る
  • その中に tsudzuri 専用のデータベースを作成
  • 本番・検証を分けたい場合も、まずは同一インスタンス内に別 DB として切る

インスタンス自体は小さめのマシンタイプを選び、

  • 自動バックアップ有効
  • 高可用性(マルチゾーン)は最初は切っておく(RTO/RPO 要件を満たせるなら後から有効化)

といったあたりで「コストと安心感のバランス」を取っています。

開始/停止のスケジューリング

さらにコストを削りたい場合、夜間や使わない時間帯に Cloud SQL を止める選択肢もあります。

Cloud Scheduler + Cloud Functions(または Cloud Run Jobs)+ Cloud SQL Admin API を組み合わせることで、

  • 「毎日 1:00 に停止」「毎朝 9:00 に起動」

といったスケジュール運用が可能です。

Service Account 設計 — CI/CD と Cloud Run / Cloud SQL

サービスアカウントは、ざっくり次の 2 役に分けています。

  1. CI/CD 用サービスアカウント
  • GitHub Actions から Artifact Registry / Cloud Run にアクセスするための SA
  • 例: roles/artifactregistry.writer, roles/run.admin, roles/iam.serviceAccountUser
  1. Cloud Run 実行用サービスアカウント
  • 実際に API コンテナが動くときに紐づける SA
  • Cloud SQL 用に roles/cloudsql.client、Secret Manager 用に roles/secretmanager.secretAccessor などを付与

CI 側の SA キーは GitHub Actions の GCP_SA_KEY シークレットとして登録し、.github/workflows/ci.yaml から secrets: inherit で渡しています。

Artifact Registry — イメージ管理とライフサイクル

コンテナイメージは Artifact Registry に格納し、GitHub Actions からタグ付きで push しています。

古いイメージでストレージ課金が増えがちなので、Artifact Registry のライフサイクルポリシーで、

  • 「最新数個だけ残し、それ以外は一定期間で削除」

といったルールを 1 つ入れておくと安心です。

Dockerfile は multi-stage build で、builder ステージでビルド → runtime ステージにバイナリだけコピーするごく基本的な構成ですが、それだけでもイメージサイズと cold start をだいぶ抑えられます。

OpenTelemetry / Logging — 見える化の土台

ファイルとしては cmd/api/otel.go, pkg/trace/, pkg/log/ あたりに設定がまとまっています。

  • gRPC / HTTP ハンドラに OpenTelemetry のミドルウェアを挿入
  • GCP のリソース情報(プロジェクト ID, サービス名など)を自動検出
  • ローカル開発時は stdout exporter、本番は Cloud Trace exporter などに切り替え可能な構成

ログは zap ベースで JSON 形式にまとめておき、Cloud Run 上ではそのまま Cloud Logging に取り込まれます。
エラー時にはトレース ID をログに埋め込むようにしておくことで、

  • 「このリクエストで何が起きたか」をログから辿る
  • 同じ ID で Cloud Trace 上のスパンも追いかける

といった、運用時に嬉しいクロス参照がしやすくなります。

「最初から観測可能性を入れておく」ことで、後からのトラブルシュートやパフォーマンスチューニングが格段に楽になります。


CI / CD パイプライン

Lint & Test(CI)

.github/workflows/lint-and-test.yaml で、feature/** ブランチへの push をトリガーに CI を走らせています。

  • make install-tools で .bin/ にツールインストール
  • make lint で静的解析
  • make test でユニットテスト

チーム開発を想定して、feature ブランチに push したら自動で検証が走る流れを最初から仕込んでいます。

Cloud Run へのデプロイ(CD)

CD は、再利用可能なワークフロー .github/workflows/ci.yaml を中心に構成しています。

  • Docker イメージをビルド
  • Artifact Registry へ push
  • Cloud Run へデプロイ
  • 必要に応じてトラフィックの切り替え

環境ごとにラッパーを用意することで、同じ CI ロジックを dev / prod で使い回せるようにしました。

  • dev-precheck.yaml
    • precheck ブランチへの push or 手動実行で dev 環境へデプロイ
  • cd.yaml
    • v*.*.* タグの push をトリガーに prod 環境へデプロイ

Cloud Run に渡す環境変数は .github/env/dev.yaml / .github/env/prod.yaml のような YAML で管理し、 ci.yaml の inputs として渡します。


GitHub Copilot / AI エージェントを使った開発フロー

今回の開発では、GitHub Copilot(特に Chat / エージェント機能)をトライアルで登録しかなり積極的に使いました。正直これが無いと厳しかったと思います。

Copilot に任せたこと

  • 既存のコードに合わせたテスト生成
    • 例: usecase/page や domain/user のテーブルドリブンテスト
  • Protobuf や ent スキーマの初期雛形
  • OpenTelemetry の設定や gRPC ミドルウェアの配線案
  • コード補完

特に、「このレイヤの責務はこうだから、ここにこういう関数を追加したい」 と説明すると、既存コードからパターンを学習してかなり良い初期案を出してくれます。

人間がやるべきこと

一方で、以下は人間側がしっかり判断したところです。

  • ドメインモデリング(Page / User / Link の境界、ユースケースの切り方)
  • トランザクション境界の設計(どこまでを 1 トランザクションにするか)
  • エラー設計(presentation/errcode の粒度やマッピング方針)
  • CI/CD・GCP 全体像の設計

Copilot はコードを書くのは得意ですが、「そもそも何を作るべきか」「どんな責務の分割が美しいか」という設計は、やはり人間が主導した方がうまくいきました。

実際のやり取りイメージ

  • 「usecase/page に Page の join ユースケースを追加したい。既存の create/list と同じテストスタイルでテストも追加して」
  • 「この gRPC ハンドラから usecase を呼ぶ部分でエラー変換がダサいので、presentation/errcode に共通関数を足して整理して」
  • 「Cloud Run 用の GitHub Actions を作りたい。.github/workflows/ci.yaml を再利用ワークフローとして切り出して」

といった依頼を投げると、コードとワークフローを書いてくれます。そこに対して、「ここはトラフィック切り替えは別でやるから enable_traffic_update: false にして」など、プロダクトの事情を踏まえた微調整を人間が行う、という形でコラボしました。


まとめ

  • 10日という短期間でも、設計と運用の「最低ライン」を最初に決めることで、後から伸ばせるバックエンド API を作れる
  • Go / gRPC / ent / OpenTelemetry / Cloud Run / GitHub Actions という組み合わせは、個人開発でも十分現実的な選択肢
  • GitHub Copilot などの AI エージェントを「ペアプロ相手」として使うと、実装や設定の初期案づくりが爆速になる

この記事が、開発を 「10日で形にしてみる」 ための参考になれば嬉しいです。

リポジトリ: https://github.com/naka-sei/tsudzuri

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?