Go言語におけるミドルウェアの設計と実装

ミドルウェアとは何か?

**ミドルウェア(Middleware)**は、高階関数として機能する設計パターンであり、ある関数を受け取り、機能を追加した新しい関数を返却します。このパターンの本質的な概念は、関数を階層的に組み合わせることで、動的に処理機能を拡張することにあります。Go言語において、ミドルウェアはログ収集、認証処理、エラー管理、データ変換などの用途で広く利用されます。

// ミドルウェア型定義:ハンドラを受け取り、強化されたハンドラを返す高階関数
type Middleware func(Handler) Handler

ミドルウェアの必要性

実際の開発では次のような要件が頻繁に発生します:

監査ログ:関数実行前後にパラメータや実行時間を記録 アクセス制御:ユーザの権限に基づきリクエストの許可判断 例外管理:実行中のエラーをキャッチして統合的に処理

ミドルウェアを使用しない場合、コードは以下のようになります:

func (svc *MyService) SampleOperation(ctx context.Context, input string) error {
    RecordLog(ctx) // ログ記録
    if authErr := ValidatePermission(ctx); authErr != nil { // 権限確認
        return authErr
    }
    return svc.ExecuteCoreLogic(ctx, input) // 核心ロジック
}

このように記述することで、ビジネスロジックと補助的な処理が混在し、保守性が低下します。

ミドルウェアを導入することで、これらの関心事を分離し、コードを簡潔かつ明確にすることができます:

func (svc *MyService) SampleOperation(ctx context.Context, input string) error {
    return svc.ExecuteCoreLogic(ctx, input) // ビジネスロジックに集中
}

ミドルウェアによる機能注入により、業務コードは単一の責任を持つようになり、可読性と保守性が向上します。

ミドルウェアの動作原理

以下3つのステップでその動作を説明します:

ミドルウェアの実装 業務ハンドラの実装 複数ミドルウェアの組み合わせとハンドラのラップ

3.1 ミドルウェアの実装

前述の抽象定義に基づき、具体的なミドルウェアを実装します。

// ミドルウェア型宣言
type Middleware func(Handler) Handler

// ロギング機能を提供するミドルウェア
func LoggingMiddleware(next Handler) Handler {
    return func(payload string) string {
        fmt.Println("開始時刻:", time.Now().Format("15:04:05"))
        outcome := next(payload) // 次の処理を呼び出し(クロージャーでnextを保持)
        fmt.Println("終了時刻:", time.Now().Format("15:04:05"))
        return outcome
    }
}

// 認証機能を提供するミドルウェア
func AuthenticationMiddleware(next Handler) Handler {
    return func(payload string) string {
        fmt.Println("    認証プロセス開始")
        if payload == "不正ユーザー" {
            return "認証失敗"
        }
        outcome := next(payload)
        fmt.Println("    認証プロセス完了")
        return outcome
    }
}

3.2 ハンドラの実装

// 実際の業務処理を担当する関数
func CoreBusinessHandler(payload string) string {
    fmt.Println("            処理対象: " + payload + " - 実行完了")
    return payload
}

3.3 ミドルウェアの連鎖とハンドラのラップ

複数のミドルウェアを同時に適用する必要がある場合、それらを適切な順序で結合する必要があります。

結合はチェーン構造(Chain)を通じて行われます。以下のコードはChain関数の実装方法を示しています:

func BuildChain(middlewareList ...Middleware) Middleware {
    return func(target Handler) Handler {
        // 後方から前方へ向けてミドルウェアをラップ
        for index := len(middlewareList) - 1; index >= 0; index-- {
            target = middlewareList[index](target)
        }
        return target
    }
}
// 1. ミドルウェアを連鎖させる
composedMiddleware := BuildChain(LoggingMiddleware, AuthenticationMiddleware)
// 2. ハンドラをラップ
processedHandler := composedMiddleware(CoreBusinessHandler)

ミドルウェアの実行プロセスは**「製造ラインでの加工」**に例えることができます:元のデータが各ミドルウェアを通過しながら段階的に処理され、最終的に完成した結果が返されます。

開始時刻: 18:21:04 認証プロセス開始 処理対象: ファイル作成 - 実行完了 認証プロセス完了 終了時刻: 18:21:04

完全な実装例

前述のコードフローを完全に示すために、以下の完全なサンプルを参照してください:

package main

import (
    "fmt"
    "time"
)

// ミドルウェア型定義:ハンドラを入力として受け取り、変換されたハンドラを出力
type Middleware func(Handler) Handler

// ログ記録ミドルウェアの実装
func LoggingMiddleware(next Handler) Handler {
    return func(payload string) string {
        fmt.Println("開始時刻:", time.Now().Format("15:04:05"))
        outcome := next(payload) // 次の処理を呼び出し(クロージャーでnextを保持)
        fmt.Println("終了時刻:", time.Now().Format("15:04:05"))
        return outcome
    }
}

// 認証ミドルウェアの実装
func AuthenticationMiddleware(next Handler) Handler {
    return func(payload string) string {
        fmt.Println("    認証プロセス開始")
        if payload == "不正ユーザー" {
            return "認証失敗"
        }
        outcome := next(payload)
        fmt.Println("    認証プロセス完了")
        return outcome
    }
}

// 複数ミドルウェアを連鎖させる関数
// 入力:n個のミドルウェア(高階関数)
// 出力:1つの合成ミドルウェア(高階関数)
// 機能:n個のミドルウェアを階層的にラップ
func BuildChain(middlewareList ...Middleware) Middleware {
    return func(target Handler) Handler {
        // 後方から前方へ向けてミドルウェアを適用
        for index := len(middlewareList) - 1; index >= 0; index-- {
            target = middlewareList[index](target)
        }
        return target
    }
}

// ハンドラ型定義:HTTPリクエストに対する実際の処理ロジックに対応
type Handler func(string) string

// 核心業務処理関数
func CoreBusinessHandler(payload string) string {
    fmt.Println("            処理対象: " + payload + " - 実行完了")
    return payload
}

// 実行例
func main() {
    composedMiddleware := BuildChain(LoggingMiddleware, AuthenticationMiddleware)
    processedHandler := composedMiddleware(CoreBusinessHandler)

    processedHandler("ファイル作成")
}

タグ: golang Middleware higher-order-function chain-of-responsibility closure

6月8日 17:05 投稿