Skip to content

Go-DDDマーケットプレイスアプリケーション 環境変数管理

1. 概要

この文書では、Go-DDDマーケットプレイスアプリケーションにおける環境変数管理の実装方法について説明します。環境変数は、アプリケーションの設定を外部化し、異なる環境(開発、テスト、本番など)で異なる設定を使用できるようにするために重要です。

本プロジェクトでは、godotenvライブラリを使用して、.envファイルから環境変数を読み込む方法を採用しています。

uml diagram

2. godotenvとは

godotenvは、Goアプリケーションで.envファイルから環境変数を読み込むためのライブラリです。これにより、以下のメリットが得られます:

  1. 設定の外部化: アプリケーションコードから設定を分離
  2. 環境ごとの設定: 開発、テスト、本番など異なる環境で異なる設定を使用可能
  3. 機密情報の保護: APIキーやデータベースパスワードなどの機密情報をコードから分離
  4. デプロイの簡素化: 環境変数を変更するだけで、アプリケーションの再ビルドなしに設定を変更可能

3. インストール方法

godotenvは以下のコマンドでインストールできます:

go get github.com/joho/godotenv

4. 基本的な使用方法

4.1 .envファイルの作成

プロジェクトのルートディレクトリに.envファイルを作成します:

# データベース設定
DB_HOST=localhost
DB_PORT=9920
DB_USER=root
DB_PASSWORD=password
DB_NAME=mydb
DB_SSL_MODE=disable

# APIサーバー設定
API_PORT=9090
API_SECRET=your_secret_key

# JWT設定
JWT_SECRET=your_jwt_secret
JWT_EXPIRATION=24h

注意: .envファイルはGitリポジトリにコミットしないでください。.gitignoreファイルに.envを追加して、機密情報が公開されないようにしてください。

4.2 環境変数の読み込み

アプリケーションの起動時に.envファイルから環境変数を読み込みます:

package main

import (
    "log"
    "os"

    "github.com/joho/godotenv"
)

func main() {
    // .envファイルを読み込む
    err := godotenv.Load()
    if err != nil {
        log.Println("Warning: .env file not found, using OS environment variables")
    }

    // 環境変数を取得
    dbHost := os.Getenv("DB_HOST")
    dbPort := os.Getenv("DB_PORT")
    apiPort := os.Getenv("API_PORT")

    // デフォルト値の設定
    if apiPort == "" {
        apiPort = "9090" // デフォルトポート
    }

    // 以降の処理...
}

4.3 複数の環境に対応

異なる環境(開発、テスト、本番)に対応するために、複数の.envファイルを使用できます:

func loadEnv() {
    env := os.Getenv("GO_ENV")
    if env == "" {
        env = "development" // デフォルト環境
    }

    // 環境に応じた.envファイルを読み込む
    envFile := ".env." + env
    err := godotenv.Load(envFile)
    if err != nil {
        // 特定の環境の.envファイルが見つからない場合、デフォルトの.envを試す
        err = godotenv.Load()
        if err != nil {
            log.Println("Warning: No .env file found, using OS environment variables")
        }
    }
}

この場合、以下のファイルを作成します: - .env.development - 開発環境用 - .env.test - テスト環境用 - .env.production - 本番環境用

5. Go-DDDマーケットプレイスでの実装

5.1 設定管理パッケージの作成

環境変数を一元管理するための設定管理パッケージを作成します:

// internal/infrastructure/config/config.go
package config

import (
    "log"
    "os"
    "strconv"
    "time"

    "github.com/joho/godotenv"
)

// Config はアプリケーション設定を保持する構造体
type Config struct {
    // データベース設定
    DBHost     string
    DBPort     string
    DBUser     string
    DBPassword string
    DBName     string
    DBSSLMode  string

    // APIサーバー設定
    APIPort string

    // JWT設定
    JWTSecret     string
    JWTExpiration time.Duration
}

// LoadConfig は環境変数から設定を読み込む
func LoadConfig() *Config {
    // .envファイルを読み込む
    err := godotenv.Load()
    if err != nil {
        log.Println("Warning: .env file not found, using OS environment variables")
    }

    // JWTの有効期限を解析
    jwtExp := os.Getenv("JWT_EXPIRATION")
    var jwtExpDuration time.Duration
    if jwtExp != "" {
        jwtExpDuration, err = time.ParseDuration(jwtExp)
        if err != nil {
            log.Printf("Warning: Invalid JWT_EXPIRATION format: %v, using default 24h", err)
            jwtExpDuration = 24 * time.Hour
        }
    } else {
        jwtExpDuration = 24 * time.Hour // デフォルト24時間
    }

    return &Config{
        // データベース設定
        DBHost:     getEnv("DB_HOST", "localhost"),
        DBPort:     getEnv("DB_PORT", "9920"),
        DBUser:     getEnv("DB_USER", "root"),
        DBPassword: getEnv("DB_PASSWORD", "password"),
        DBName:     getEnv("DB_NAME", "mydb"),
        DBSSLMode:  getEnv("DB_SSL_MODE", "disable"),

        // APIサーバー設定
        APIPort: getEnv("API_PORT", "9090"),

        // JWT設定
        JWTSecret:     getEnv("JWT_SECRET", "default_jwt_secret"),
        JWTExpiration: jwtExpDuration,
    }
}

// getEnv は環境変数を取得し、設定されていない場合はデフォルト値を返す
func getEnv(key, defaultValue string) string {
    value := os.Getenv(key)
    if value == "" {
        return defaultValue
    }
    return value
}

// GetDSN はデータベース接続文字列を生成する
func (c *Config) GetDSN() string {
    return "host=" + c.DBHost + 
           " user=" + c.DBUser + 
           " password=" + c.DBPassword + 
           " dbname=" + c.DBName + 
           " port=" + c.DBPort + 
           " sslmode=" + c.DBSSLMode + 
           " TimeZone=Asia/Shanghai"
}

// GetAPIPort はAPIポートを取得する(":"プレフィックス付き)
func (c *Config) GetAPIPort() string {
    return ":" + c.APIPort
}

5.2 メイン関数での使用

アプリケーションのメイン関数で設定を読み込み、使用します:

// cmd/marketplace/main.go
package main

import (
    "log"

    "github.com/labstack/echo/v4"
    "gorm.io/driver/postgres"
    "gorm.io/gorm"

    "github.com/yourusername/go-ddd/internal/application/services"
    "github.com/yourusername/go-ddd/internal/infrastructure/config"
    postgres2 "github.com/yourusername/go-ddd/internal/infrastructure/db/postgres"
    "github.com/yourusername/go-ddd/internal/interface/api/rest"
)

func main() {
    // 設定を読み込む
    cfg := config.LoadConfig()

    // データベース接続
    gormDB, err := gorm.Open(postgres.Open(cfg.GetDSN()), &gorm.Config{})
    if err != nil {
        log.Fatalf("Failed to connect to database: %v", err)
    }

    // データベースマイグレーションを実行
    if err := postgres2.RunMigrations(gormDB); err != nil {
        log.Fatalf("Failed to run migrations: %v", err)
    }

    // リポジトリの初期化
    productRepo := postgres2.NewGormProductRepository(gormDB)
    sellerRepo := postgres2.NewGormSellerRepository(gormDB)

    // サービスの初期化
    productService := services.NewProductService(productRepo, sellerRepo)
    sellerService := services.NewSellerService(sellerRepo)

    // Echoサーバーの初期化
    e := echo.New()

    // ルーターの設定
    rest.SetupRouter(e, productService, sellerService)

    // サーバーの起動
    if err := e.Start(cfg.GetAPIPort()); err != nil {
        log.Fatalf("Failed to start server: %v", err)
    }
}

6. テスト環境での使用

テスト時には、テスト用の環境変数を設定するために.env.testファイルを使用できます:

// internal/infrastructure/config/config_test.go
package config

import (
    "os"
    "testing"
    "time"

    "github.com/joho/godotenv"
)

func TestLoadConfig(t *testing.T) {
    // テスト用の環境変数を設定
    os.Setenv("DB_HOST", "test-host")
    os.Setenv("API_PORT", "8080")
    os.Setenv("JWT_EXPIRATION", "1h")

    // 設定を読み込む
    cfg := LoadConfig()

    // 設定値を検証
    if cfg.DBHost != "test-host" {
        t.Errorf("Expected DBHost to be 'test-host', got '%s'", cfg.DBHost)
    }

    if cfg.APIPort != "8080" {
        t.Errorf("Expected APIPort to be '8080', got '%s'", cfg.APIPort)
    }

    if cfg.JWTExpiration != time.Hour {
        t.Errorf("Expected JWTExpiration to be 1h, got '%v'", cfg.JWTExpiration)
    }

    // 環境変数をクリア
    os.Unsetenv("DB_HOST")
    os.Unsetenv("API_PORT")
    os.Unsetenv("JWT_EXPIRATION")
}

func TestGetDSN(t *testing.T) {
    cfg := &Config{
        DBHost:     "localhost",
        DBPort:     "5432",
        DBUser:     "testuser",
        DBPassword: "testpass",
        DBName:     "testdb",
        DBSSLMode:  "disable",
    }

    expected := "host=localhost user=testuser password=testpass dbname=testdb port=5432 sslmode=disable TimeZone=Asia/Shanghai"
    if dsn := cfg.GetDSN(); dsn != expected {
        t.Errorf("Expected DSN to be '%s', got '%s'", expected, dsn)
    }
}

7. ベストプラクティス

7.1 .envファイルのテンプレート

.envファイルをGitリポジトリにコミットしないため、.env.exampleファイルを作成して、必要な環境変数のテンプレートを提供することをお勧めします:

# .env.example
# このファイルをコピーして.envファイルを作成し、適切な値を設定してください

# データベース設定
DB_HOST=localhost
DB_PORT=9920
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=mydb
DB_SSL_MODE=disable

# APIサーバー設定
API_PORT=9090
API_SECRET=your_secret_key

# JWT設定
JWT_SECRET=your_jwt_secret
JWT_EXPIRATION=24h

7.2 環境変数の検証

アプリケーション起動時に必須の環境変数が設定されているか検証することをお勧めします:

// 必須環境変数の検証
func validateRequiredEnv() error {
    required := []string{"DB_HOST", "DB_USER", "DB_PASSWORD", "JWT_SECRET"}
    missing := []string{}

    for _, r := range required {
        if os.Getenv(r) == "" {
            missing = append(missing, r)
        }
    }

    if len(missing) > 0 {
        return fmt.Errorf("Missing required environment variables: %v", missing)
    }

    return nil
}

7.3 機密情報の管理

機密情報(パスワード、APIキーなど)の管理には以下のプラクティスを推奨します:

  1. 開発環境では.envファイルを使用
  2. テスト環境ではCI/CDシステムの環境変数機能を使用
  3. 本番環境ではKubernetesのSecretsやAWS Parameter Storeなどの専用サービスを使用

7.4 .gitignoreの設定

.gitignoreファイルに以下を追加して、環境変数ファイルがリポジトリにコミットされないようにします:

# 環境変数ファイル
.env
.env.*
!.env.example

8. まとめ

godotenvを使用した環境変数管理は、Go-DDDマーケットプレイスアプリケーションの設定を外部化し、異なる環境での実行を容易にします。この実装により、以下のメリットが得られます:

  1. 設定の一元管理: すべての設定が一箇所で管理され、変更が容易
  2. 環境ごとの設定: 開発、テスト、本番環境で異なる設定を使用可能
  3. セキュリティの向上: 機密情報をコードから分離
  4. デプロイの簡素化: 環境変数を変更するだけで設定を変更可能

godotenvを使用することで、12-Factorアプリケーションの「設定」の原則に従い、より堅牢で保守性の高いアプリケーションを構築できます。