mcpField
G
golang-google-wire

golang-google-wire

github.com/samber開発3.7k37.2k 回インストール5 日前に更新
リポジトリ

golang-google-wire について

Go 開発者向けの google/wire ガイドです。コンパイル時に依存関係グラフを解決し、通常の Go 呼び出しを生成して実行前に不足依存を検出します。Provider、Injector、クリーンアップ、コード生成の実践を扱います。

主な機能

  1. 01コンパイル時の依存解決
  2. 02通常の Go コンストラクター生成
  3. 03再利用可能な Provider セット
  4. 04明示的なインターフェースバインディング
  5. 05クリーンアップ関数の連鎖
  6. 06コード生成とグラフ検証

活用シーン

  1. 01Go サービスの依存関係の構成
  2. 02ビルド前の依存グラフ検証
  3. 03グラフ変更後のコード生成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/samber/cc-skills-golang --skill golang-google-wire

インストール後、Claude Code で/golang-google-wireを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

evals/
evals.json
references/
advanced.md
recipes.md
testing.md

詳細

ペルソナ: コンパイル時 DI に wire を使用する Go アーキテクトです。コンパイラーに依存関係の不足を検出させ、wire_gen.go をコミット対象のソースとして扱い、依存関係グラフを変更するたびに wire ./... を再実行します。

依存関係:

  • wire:go install github.com/google/wire/cmd/wire@latest

Go でコンパイル時依存性注入に google/wire を使用する

コード生成による DI ツールキットです。Wire はコンパイル時に依存関係グラフを解決し、通常の Go コンストラクター呼び出しを出力します。実行時のコンテナーもリフレクションもありません。エラーは最初のリクエスト時ではなく、wire ./... の実行時に表示されます。

注:google/wire は 2025 年 8 月にアーカイブされました(機能は完成しており、バグ修正は引き続き受け付けています)。

公式リソース: pkg.go.dev · github.com/google/wire · ユーザーガイド · ベストプラクティス

このスキルは網羅的ではありません。詳細については、ライブラリのドキュメントとコード例を参照してください。

  • Go パッケージのドキュメント、シンボル、バージョン、インポーター、既知の脆弱性については、→ samber/cc-skills-golang@golang-pkg-go-dev スキル(godig)を参照してください。Go パッケージに関する情報では、Context7 よりこちらが推奨されます。
  • ご自身のコードでこのライブラリの使い方(定義、呼び出し箇所、診断情報)を確認するには、→ samber/cc-skills-golang@golang-gopls スキル(gopls)を参照してください。
  • pkg.go.dev でインデックス化されていないドキュメントについては、引き続き Context7 をフォールバックとして使用できます。
go get -tool github.com/google/wire/cmd/wire@latest
go get github.com/google/wire

wire と実行時 DI の比較

項目wiredig / fx / samber/do
解決コンパイル時(コード生成)実行時(リフレクション)
エラー検出wire ./... が失敗最初の Invoke/起動時
実行時コンテナーなし — 通常の Go 呼び出しあり
ライフサイクルフック組み込みではないfx:OnStart/OnStop
生成ファイルwire_gen.go(コミット対象)なし

ライフサイクル、遅延読み込み、比較表の全項目については、samber/cc-skills-golang@golang-dependency-injection を参照してください。

プロバイダー

プロバイダーとは、任意の Go 関数です。入力は依存関係であり、出力は提供される型です。戻り値には 3 つの形式があります。

func NewConfig() *Config                          { return &Config{Addr: ":8080"} }
func NewDB(cfg *Config) (*sql.DB, error)          { return sql.Open("postgres", cfg.DSN) }
func NewRedis(cfg *Config) (*redis.Client, func(), error) { // cleanup chained in reverse order
    c := redis.NewClient(&redis.Options{Addr: cfg.RedisAddr})
    return c, func() { c.Close() }, nil
}

プロバイダーセット

wire.NewSet はプロバイダーをまとめ、再利用できるようにします。セットは他のセットを参照できます。

// infra/wire.go
var InfraSet = wire.NewSet(
    NewConfig,
    NewDB,
    NewRedis,
)

// service/wire.go
var ServiceSet = wire.NewSet(
    NewUserRepo,
    NewUserService,
    wire.Bind(new(UserStore), new(*UserRepo)), // interface binding
)

セットは小さく保ちます。ライブラリのセットは安定したインターフェイスを公開するため、入力を追加したり出力を削除したりすると、下流のインジェクターが動作しなくなります。パッケージごとに 1 つのセットを設けるのが、実用的なデフォルトです。

インジェクターと //go:build wireinject

インジェクターファイルで初期化関数を宣言します。Wire はその本体を wire_gen.go に生成し、スタブを置き換えます。

//go:build wireinject

package main

import "github.com/google/wire"

// Wire generates the body of this function.
func InitApp() (*App, func(), error) {
    wire.Build(InfraSet, ServiceSet, NewApp)
    return nil, nil, nil // replaced by codegen
}

//go:build wireinject ビルドタグを付けると、スタブがバイナリーにコンパイルされなくなります。go build を通るのは、このタグが付いていない wire_gen.go のみです。このタグがないと、両方のファイルで同じ関数が定義され、コンパイルエラーになります。

ダミーの戻り値が不都合な場合は、次の構文も使用できます。

func InitApp() (*App, func(), error) {
    panic(wire.Build(InfraSet, ServiceSet, NewApp))
}

インターフェイスのバインディング

Wire は暗黙のインターフェイス実装を許可しません。同じインターフェイスを実装する型が複数ある場合でも依存関係グラフが曖昧にならないよう、バインディングを明示的に宣言する必要があります。

var Set = wire.NewSet(
    NewPostgresUserRepo,
    wire.Bind(new(UserStore), new(*PostgresUserRepo)), // tell wire: *PostgresUserRepo satisfies UserStore
)

明示的にバインディングを宣言することで、別の場所に同じインターフェイスを実装する新しい型が追加されても、依存関係グラフが壊れるのを防げます。

構造体プロバイダーと値

wire.Struct を使うと、手動でコンストラクターを用意せずに、依存関係グラフから構造体のフィールドを設定できます。除外するフィールドには wire:"-" タグを付けます。

wire.Struct(new(Server), "Logger", "DB") // inject named fields
wire.Struct(new(Server), "*")            // inject all non-excluded fields
wire.Value(Foo{X: 42})                   // constant expression (no fn calls / channels)
wire.InterfaceValue(new(io.Reader), os.Stdin) // interface-typed literal
wire.FieldsOf(new(Config), "DSN", "Addr")    // promote struct fields as graph nodes

wire:"-" 除外タグと wire.FieldsOf の詳細については、advanced.md を参照してください。

重複する型の区別

Wire は同じ型に対する 2 つのプロバイダーを許可しません。基になる型をそれぞれ異なる名前付き型でラップし、各型にプロバイダーが 1 つだけ存在するようにします。

type PrimaryDSN string
type ReplicaDSN string

アプリケーション全体の例

// wire.go — injector, excluded from binary via build tag
//go:build wireinject

package main

func InitApp() (*App, func(), error) {
    wire.Build(config.ConfigSet, infra.InfraSet, service.ServiceSet, NewApp)
    return nil, nil, nil
}

// main.go
func main() {
    app, cleanup, err := InitApp()
    if err != nil { log.Fatal(err) }
    defer cleanup()
    app.Run()
}

Wire は wire_gen.go(通常の Go コード。コミット対象。編集禁止)を生成します。パッケージごとのセット、クリーンアップが多いグラフ、生成された出力を含む完全な例については、recipes.md を参照してください。

コード生成のワークフロー

wire ./...           # regenerate all injectors in the module
wire check ./...     # validate graph without regenerating (fast CI check)

コンストラクターのシグネチャを変更するたびに、wire ./... を実行してください。インジェクターファイルに //go:generate go run github.com/google/wire/cmd/wire を追加すると、go generate ./... でも実行できます。wire_gen.go をコミットしてください。CI ビルドで同期された状態を保つ必要があります。

ベストプラクティス

  1. wire_gen.go は編集しないでください。wire ./... を実行するたびに上書きされます。コミット対象ではありますが、ビルド成果物として扱ってください。信頼できる情報源はプロバイダーとインジェクターのファイルです。
  2. インジェクターファイルには必ず //go:build wireinject を追加してください。省略すると、スタブと生成ファイルの両方で同じ関数が定義されるため、シンボルの重複によるコンパイルエラーが発生します。
  3. 基底型が同じ値を区別するには、名前付き型を使用してください。Wire は型ごとに 1 つのプロバイダーを強制するため、type DSN string のような名前付き型を使えば、PrimaryDSN と ReplicaDSN を共存させられます。
  4. ライブラリのプロバイダーセットは最小限に保ち、後方互換性を維持してください。新しい必須入力を追加すると下流のインジェクターが壊れ、出力を削除しても同様です。同じリリースで新たに作成された型のみを導入してください。
  5. クリーンアッププロバイダーから (T, func(), error) を返し、Wire に連結させてください。Wire は正しい逆順でクリーンアップを生成し、途中で構築に失敗した場合にも対応します(構築済みのクリーンアップのみが実行されます)。
  6. インジェクターファイルの役割を絞ってください。1 ファイルにつき関数 1 つ、パッケージのインポートも一度に 1 つにします。多数の wire.Build 引数を持つ肥大化したインジェクターは理解しにくいため、パッケージごとのセットに委譲してください。

よくある間違い

間違い修正方法
wire_gen.go を手動で編集する編集しないでください。プロバイダーまたはインジェクターを変更し、wire ./... を再実行してください。
//go:build wireinject の付け忘れすべてのインジェクターファイルの先頭行にタグを追加してください。
*sql.DB を返すプロバイダーが 2 つある名前付き構造体型でラップしてください:type PrimaryDB struct { *sql.DB } — Wire はポインター型のエイリアスを区別しません。
wire.Bind を使わずにインターフェースを注入するプロバイダーセットに wire.Bind(new(MyInterface), new(*MyImpl)) を追加してください。
変更後に wire ./... を再実行し忘れるgo build の前に Wire を実行してください。go generate または Makefile のターゲットに追加してください。
nil を確認せずに cleanup() を呼び出す構築エラー時、Wire は nil のクリーンアップ関数を返します。if cleanup != nil { defer cleanup() } で確認してください。

テスト

Wire は通常の Go コンストラクターを生成するため、ユニットテストでは手動で注入します。複製やリセットが必要なコンテナーはありません。テストのパターン(実際のプロバイダーをフェイクに置き換えるテスト用インジェクター、wire_gen.go の CI 同期チェック)については、testing.md を参照してください。

参考資料

  • advanced.md — クリーンアップチェーン、複数のインジェクター、セットのネスト、エラーカタログ、コード生成フラグ、クイックリファレンス
  • recipes.md — HTTP サーバー、複数インジェクターのビルド、クリーンアップが多いグラフ、CLI への埋め込み
  • testing.md — テスト用インジェクター、フェイクバインディング、CI 同期チェック

関連項目

  • → DI の概念とライブラリの比較については、samber/cc-skills-golang@golang-dependency-injection スキルを参照してください
  • → ライフサイクルを伴わない、ランタイムのリフレクションベース DI については、samber/cc-skills-golang@golang-uber-dig スキルを参照してください
  • → ライフサイクルフック、モジュール、シグナルに対応した Run() を備えるランタイム DI については、samber/cc-skills-golang@golang-uber-fx スキルを参照してください
  • → リフレクションを使わないジェネリクスベース DI については、samber/cc-skills-golang@golang-samber-do スキルを参照してください
  • → インターフェース設計パターンについては、samber/cc-skills-golang@golang-structs-interfaces スキルを参照してください
  • → 一般的なテストのパターンについては、samber/cc-skills-golang@golang-testing スキルを参照してください

google/wire でバグや予期しない動作に遭遇した場合は、https://github.com/google/wire/issues で issue を作成してください。

コメント