Skip to content

バックエンドのpackage-per-feature構造

背景

known-issues(旧・app/app二重ネスト等)に加え、今後TDD+AIエージェント主導開発に移行する予定で、現状バックエンドにテストは一切ない。ドメインロジックの抽出は単なる整理ではなく、テスト可能な境界を作る前提条件だった。

採用方針

  • package-per-feature(各機能パッケージにtransportファイルと同一パッケージ内のservice.goを並置)。2バイナリ規模のリポジトリにはinternal/domain|service|repositoryのような重量級レイヤードツリーは過剰と判断し採用しなかった。
  • DIコンテナ(google/wire、goforj/wire)は一時採用したが、依存の規模が小さく(app/workerとも依存6〜7個程度)、wire.go/wire_gen.go/providers.goの3ファイルを跨いで初期化順序を追う必要があるのがLSPでのエンドポイント探索の妨げになっていたため、後に剥がしてmain.go内での手書き組み立てに戻した(2026-08-16)。
  • ディレクトリ移動(app/cmd/appworker/cmd/workermodules/{auth,s3}internal/)は、ドメインロジック抽出と同時に一括実施した。

TDD対応の設計

  • service.goの関数はプレーンなGo引数/戻り値のみとし、echo.ContextやTUSフック型を持たず、直接ユニットテスト可能。
  • DB(sqlc生成の*db.Queries)やRiver(InsertTx)に依存する箇所は、利用側パッケージで定義する狭いインターフェース(例: internal/worksrepointernal/transcoderepointernal/uploadsriverInserter)を挟み、フェイク/スタブ差し替えを可能にしている。テーブル単位のリポジトリ抽象化のような重い抽象は導入していない。
  • internal/uploads/service.govalidateUploadableのように、DBアクセスを伴わない純粋なビジネスルールはそのまま単体でテスト可能な関数として切り出している。
  • _test.goはGo標準の慣習通り、対象コードと同一パッケージ内に配置する想定(現状未執筆、別途フォローアップ)。
  • sqlcクエリは実Postgresを叩くため、テスト用フィクスチャ戦略(transactionロールバック等)は別途フォローアップ。

現在のツリー

backend/
├── cmd/
│ ├── app/ # main.go, router.go, air.toml, Dockerfile, dashboard/
│ └── worker/ # main.go, air.toml, Dockerfile
├── internal/
│ ├── works/ # handler.go(Echoハンドラ)+ service.go(replaceTags, publishOrPend)
│ ├── uploads/ # tus_handler.go(TUSトランスポート)+ service.go(createPendingUpload, completeTUSUpload, validateUploadable)
│ ├── transcode/ # worker.go(river.Worker実装、薄い)+ service.go(completeTranscode)
│ ├── auth/ # Firebase認証(旧 app/modules/auth)
│ └── s3/ # S3クライアント(旧 app/modules/s3)
├── jobs/ # 変更なし(app/workerが依存を持ち込まずimportできる薄いパッケージの模範)
├── .secrets/ # firebase-credentials.json(gitignore対象)
└── db/ # schema/queries/genは変更なし。db/query/(空の迷子ディレクトリ)は削除済み

依存関係の組み立て

DIコンテナは使わず、cmd/appcmd/workerそれぞれのmain.go冒頭で依存関係(DBプール・Queries・Firebase認証・S3クライアント・各ハンドラ等)をローカル変数として上から順に組み立てる。組み立て済みの依存はstructにまとめず、main()内でそのままルーティング・起動処理に使う。DBプールのcleanupは生成直後にdeferで解放し、起動時のエラーはpanicで即座に落とす。

個別対応の結果

  • firebase-credentials.jsonbackend/.secrets/firebase-credentials.json(gitignore対象)に移動済み。
  • backend/postgres/backend/garage/{garage-data,garage-meta}はリポジトリルート直下の.data/(gitignore対象)へ移動済み。compose.ymlのボリュームマウントも更新済み。

フォローアップ(未着手)

  • 実際の_test.go執筆
  • DBフィクスチャ戦略
  • Flutter側のバージョン管理方式の見直し