mcpField
G
go-design-patterns

go-design-patterns

github.com/eduardo-sl開発83152 回インストール2 週間前に更新
リポジトリ

go-design-patterns について

Go 開発者向けに、構築、振る舞い、耐障害性、型設計の慣用パターンを案内するスキルです。用途別の選択ガイドと、必要に応じて参照できる詳細資料を備えています。

主な機能

  1. 01設計上のニーズに応じたパターン選択
  2. 02関数オプションとコンストラクター
  3. 03ファクトリーとミドルウェアの合成
  4. 04リトライ、サーキットブレーカー、バルクヘッド
  5. 05有効な状態を表す型

活用シーン

  1. 01Go API の設定方式の選定
  2. 02依存先呼び出しへの耐障害性の追加
  3. 03ミドルウェアとデコレーターの構成
  4. 04検証済みの値とライフサイクルの表現

インストール

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

$npx skills add https://github.com/eduardo-sl/go-agent-skills --skill go-design-patterns

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

同梱ファイル

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

references/
behavioral-patterns.md
creation-patterns.md
resilience-and-types.md

詳細

Go のデザインパターン

Go は継承よりもコンポジションを、抽象化よりもシンプルさを重視します。 これらのパターンは Go に慣用的なものであり、Java のパターンを Go に移植したものではありません。

コミュニティの標準設定です。go-design-patterns を明示的に 優先するプロジェクトスキルまたはエージェント設定ルールがある場合は、そちらが優先されます。

詳細なリファレンス資料は、必要に応じて読み込みます。

  • references/creation-patterns.md — 関数オプション(完全な例)、 オプションと設定構造体の比較、コンストラクター、ファクトリー。
  • references/behavioral-patterns.md — ストラテジー、ミドルウェア/デコレーター、 結果型、defer によるクリーンアップ、センチネル値とゼロ値の比較。
  • references/resilience-and-types.md — ジッター付きリトライ、サーキットブレーカー、 バルクヘッド、検証ではなくパースを行う型、ステータス型。

以下の概要だけでは不十分な場合にのみ、リファレンスファイルを読んでください。

パターンの選択

必要なことパターンリファレンス
多数のオプション設定を持つコンストラクター関数オプションcreation-patterns.md
ファイル/環境変数から読み込む、必須フィールドが大半の設定設定構造体creation-patterns.md
作成時に不変条件を適用するエラーを返すコンストラクターcreation-patterns.md
実行時設定から実装を選ぶインターフェースを返すファクトリーcreation-patterns.md
実行時に単純な動作を切り替える関数型によるストラテジーbehavioral-patterns.md
実行時に複雑な動作を切り替えるインターフェースによるストラテジーbehavioral-patterns.md
横断的関心事(ログ、キャッシュ、メトリクス)をラップするミドルウェア/デコレーターbehavioral-patterns.md
並行パイプラインで値またはエラーを扱うResult[T] 構造体behavioral-patterns.md
依存先で一時的な障害が発生するバックオフとジッターを使ったリトライresilience-and-types.md
依存先がダウンしているサーキットブレーカー、バルクヘッドresilience-and-types.md
一度だけ検証し、その後は信頼する非公開フィールドを持つ型へのパースresilience-and-types.md
1 つのライフサイクルを複数のブール値で表している明示的な遷移を持つステータス型resilience-and-types.md

1. 関数オプション(要点)

type Option func(*Server)

func WithAddr(addr string) Option {
    return func(s *Server) { s.addr = addr }
}

func NewServer(opts ...Option) *Server {
    s := &Server{
        addr:        ":8080", // sensible defaults first
        readTimeout: 5 * time.Second,
        logger:      slog.Default(),
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}

srv := NewServer(WithAddr(":9090"))

次の場合に使用します:適切なデフォルト値を持つ多数のオプションパラメーターがあり、API が時間とともに進化する(新しいオプションを追加しても呼び出し側に影響しない)、オプションの検証が必要。 ほとんどのフィールドが必須の場合、または設定をファイル/環境変数からデシリアライズする場合は、通常の設定構造体を使用します。

2. コンストラクターのルール

  • 不変条件を持つエクスポートされた型には、すべてコンストラクターが必要です。
  • 必須の依存関係を検証し、パニックさせずにエラーを返します。
// ✅ Good — constructor enforces invariants
func NewUserService(repo UserRepository, logger *slog.Logger) (*UserService, error) {
    if repo == nil {
        return nil, errors.New("user service: nil repository")
    }
    return &UserService{repo: repo, logger: logger}, nil
}

// ❌ Bad — struct literal with no validation
svc := &UserService{} // nil dependencies → panic at runtime

3. ファクトリー

具体型ではなく、インターフェースを返します。具体的な実装を把握するのはファクトリーだけです。

func NewStore(cfg Config) (Store, error) {
    switch cfg.StoreType {
    case "redis":
        return newRedisStore(cfg.RedisAddr)
    case "memory":
        return newMemoryStore(), nil
    default:
        return nil, fmt.Errorf("unknown store type: %s", cfg.StoreType)
    }
}

4. ミドルウェアチェーン

標準的な HTTP コンポジションパターンです。

type Middleware func(http.Handler) http.Handler

func Chain(handler http.Handler, middlewares ...Middleware) http.Handler {
    for i := len(middlewares) - 1; i >= 0; i-- {
        handler = middlewares[i](handler)
    }
    return handler
}

handler := Chain(appHandler, Recoverer, RequestID, Logger, Auth)

同じ形式はどのインターフェースにも使えます。デコレーターを cache → logging → metrics → actual repo のように重ねます(references/behavioral-patterns.md を参照)。

5. ゼロ値を優先する

ゼロ値が有用な型(sync.Mutex、bytes.Buffer、 nil スライス)を優先します。ゼロ値が入力として曖昧な場合に限り、センチネル値のラッパーやポインターを使用します(nil *float64 = 「未設定」)。

6. レジリエンス

  • すべてのアウトバウンド呼び出しには、ctx による期限を設定します(予算の管理は go-context が担います)。
  • 安全に再試行できる処理だけを、指数バックオフとフルジッター、上限付きで、1 つのレイヤーでのみ再試行します。再試行を入れ子にすると回数が倍増します。
  • サーキットブレーカーは依存先がダウンしている間、すぐに失敗させます。メンテナンスされている実装を使用し、サーキットが開いている場合は、キャッシュ値や部分的なレスポンスなどにフォールバックします。
  • バルクヘッド(依存先ごとのセマフォ)は、1 つの遅い依存先がすべてのゴルーチンと接続を使い果たすのを防ぎます。

7. 不正な状態を表現できないようにする

入力を一度だけ、非公開フィールドを持つ型(ParseEmail → Email)にパースします。これにより、その型を受け取るすべての関数が値を信頼できます。 ブール値のまとまり(Paid、Shipped、Cancelled)は、状態遷移を検査するメソッドを持つ 1 つのステータス型に置き換えます。

セクション 6〜7 の例:references/resilience-and-types.md。

避けるべきアンチパターン

// ❌ God interface — too many methods
type Service interface {
    GetUser(ctx context.Context, id string) (*User, error)
    CreateUser(ctx context.Context, u *User) error
    DeleteUser(ctx context.Context, id string) error
    ListOrders(ctx context.Context, userID string) ([]Order, error)
    // 20 more methods...
}
// → Split into focused interfaces: UserReader, UserWriter, OrderLister

// ❌ Premature abstraction — interface for one implementation
type UserCache interface {
    Get(key string) (*User, bool)
    Set(key string, user *User)
}
// If there's only ever one implementation, use the concrete type.
// Extract an interface when a second consumer or implementation appears.

// ❌ Java-style inheritance simulation
type BaseService struct{ /* ... */ }
type UserService struct{ BaseService } // embedding is NOT inheritance
// → Use composition: UserService has a dependency, not a parent.

検証チェックリスト

  1. 設定が任意の型には関数型オプションを使用する
  2. コンストラクターで必須の依存関係を検証し、エラーを返す
  3. ファクトリー関数は具象型ではなくインターフェースを返す
  4. 巨大なインターフェースを作らない — 各インターフェースのメソッドは1〜3個にする
  5. ミドルウェアは func(http.Handler) http.Handler のシグネチャに従う
  6. デコレーターは具象型ではなくインターフェースをラップする
  7. すべてのリソース(ファイル、接続、ロック)のクリーンアップには defer を使用する
  8. ゼロ値に意味を持たせる — 不要な初期化は行わない
  9. 早まった抽象化を避ける — インターフェースは必要になった場合にのみ切り出す
  10. コード再利用には埋め込みではなく合成を使用する
  11. 外部への呼び出しには期限を設定する。再試行にはジッターを適用し、上限を設け、単一のレイヤーでのみ行う
  12. 検証済みの値とライフサイクルは、再検証したりブール値に分散させたりせず、型に表現する

コメント