バックエンドの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/app、worker/→cmd/worker、modules/{auth,s3}→internal/)は、ドメインロジック抽出と同時に一括実施した。
TDD対応の設計
service.goの関数はプレーンなGo引数/戻り値のみとし、echo.ContextやTUSフック型を持たず、直接ユニットテスト可能。- DB(sqlc生成の
*db.Queries)やRiver(InsertTx)に依存する箇所は、利用側パッケージで定義する狭いインターフェース(例:internal/worksのrepo、internal/transcodeのrepo、internal/uploadsのriverInserter)を挟み、フェイク/スタブ差し替えを可能にしている。テーブル単位のリポジトリ抽象化のような重い抽象は導入していない。 internal/uploads/service.goのvalidateUploadableのように、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/app・cmd/workerそれぞれのmain.go冒頭で依存関係(DBプール・Queries・Firebase認証・S3クライアント・各ハンドラ等)をローカル変数として上から順に組み立てる。組み立て済みの依存はstructにまとめず、main()内でそのままルーティング・起動処理に使う。DBプールのcleanupは生成直後にdeferで解放し、起動時のエラーはpanicで即座に落とす。
個別対応の結果
firebase-credentials.jsonはbackend/.secrets/firebase-credentials.json(gitignore対象)に移動済み。backend/postgres/・backend/garage/{garage-data,garage-meta}はリポジトリルート直下の.data/(gitignore対象)へ移動済み。compose.ymlのボリュームマウントも更新済み。
フォローアップ(未着手)
- 実際の
_test.go執筆 - DBフィクスチャ戦略
- Flutter側のバージョン管理方式の見直し