【Go】フォルダ構成のリファクタリングとweb frameworkの比較

はじめまして！フリーランスエンジニアのこたろうです。
アプリケーションのフォルダ構成の改善とWebフレームワークの比較について、学びで得た知見を共有します。

## フォルダ構成の改善

### 1. なぜCORS設定とルーティングを分離するのか？

プログラムを書く際、1つのファイルに多くの処理を書くと、以下の問題が発生します：
- コードが読みにくくなる
- 修正が難しくなる
- 他の人が理解しにくい

そこで、CORS設定とルーティングを別ファイルに分けることで：
- 各ファイルの役割が明確になる
- コードが整理され、読みやすくなる
- 修正が容易になる

#### CORS設定の分離例
```go
// config/cors.go

// CORSとは？
// Cross-Origin Resource Sharing（CORS）は、
// 異なるドメインからのアクセスを制御する仕組みです。
// 例：localhost:3000のフロントエンドから
//     localhost:8080のバックエンドにアクセスする場合に必要

func CorsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // どのドメインからのアクセスを許可するか
        w.Header().Set("Access-Control-Allow-Origin", "*")
        
        // どのHTTPメソッドを許可するか
        w.Header().Set("Access-Control-Allow-Methods", 
            "GET, POST, PUT, DELETE, OPTIONS")
        
        // どのヘッダーを許可するか
        w.Header().Set("Access-Control-Allow-Headers", 
            "Content-Type, Authorization")
        
        // プリフライトリクエストの処理
        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }
        
        next.ServeHTTP(w, r)
    })
}
```

#### ルーティングの分離例
```go
// config/router.go

// ルーティングとは？
// URLと処理（関数）を紐付けることです
// 例：/api/todosにアクセスしたら、GetTodos関数を実行する

func NewRouter() *mux.Router {
    router := mux.NewRouter()
    
    // GETメソッド：データの取得
    router.HandleFunc("/api/todos", controllers.GetTodos).
        Methods("GET")
    
    // POSTメソッド：データの作成
    router.HandleFunc("/api/todos", controllers.CreateTodo).
        Methods("POST")
    
    // PUTメソッド：データの更新
    router.HandleFunc("/api/todos/{id}", controllers.UpdateTodo).
        Methods("PUT")
    
    // DELETEメソッド：データの削除
    router.HandleFunc("/api/todos/{id}", controllers.DeleteTodo).
        Methods("DELETE")
    
    return router
}
```

### 2. レイヤードアーキテクチャとは？

アプリケーションを以下の3つの層に分けて管理する方法です：

1. Controllers層（入口）
   - ユーザーからのリクエストを受け取る
   - データの形式をチェック
   - Services層に処理を依頼
   - 結果をユーザーに返す

2. Services層（ビジネスロジック）
   - アプリケーションの主要な処理を行う
   - データの加工や計算
   - 複雑なチェック処理
   - Repositories層にデータの操作を依頼

3. Repositories層（データ操作）
   - データベースとのやり取り
   - データの保存、取得、更新、削除

#### フォルダ構成例
```
├── controllers/
│   └── todo_controller.go  （リクエストの受付）
├── services/
│   └── todo_service.go     （ビジネスロジック）
└── repositories/
    └── todo_repository.go  （データベース操作）
```

#### 実装例と説明

```go
// repositories/todo_repository.go
// データベース操作を担当
type TodoRepository interface {
    FindAll() ([]Todo, error)      // 全データ取得
    FindByID(id int) (*Todo, error) // 1件取得
    Create(todo *Todo) error       // データ作成
    Update(todo *Todo) error       // データ更新
    Delete(id int) error           // データ削除
}

// services/todo_service.go
// ビジネスロジックを担当
type TodoService struct {
    repo TodoRepository  // repositoryを使用
}

func (s *TodoService) GetTodos() ([]Todo, error) {
    // 1. repositoryからデータを取得
    todos, err := s.repo.FindAll()
    if err != nil {
        return nil, err
    }
    
    // 2. 必要な加工や計算を行う
    // （例：期限切れのTODOに印をつけるなど）
    for i := range todos {
        if todos[i].DueDate.Before(time.Now()) {
            todos[i].IsExpired = true
        }
    }
    
    return todos, nil
}

// controllers/todo_controller.go
// リクエストの受付を担当
type TodoController struct {
    service *TodoService  // serviceを使用
}

func (c *TodoController) GetTodos(w http.ResponseWriter, r *http.Request) {
    // 1. リクエストのチェック
    // （必要に応じてバリデーションなど）
    
    // 2. serviceに処理を依頼
    todos, err := c.service.GetTodos()
    if err != nil {
        // エラー時の処理
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    // 3. 結果をJSONで返す
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(todos)
}
```

このように分けることで：
- コードが整理され、理解しやすくなる
- テストが書きやすくなる
- 修正が容易になる
- チーム開発がスムーズになる

## gorilla/muxとginの比較

### gorilla/mux
標準パッケージnet/httpを拡張したシンプルなフレームワーク

#### メリット
1. 学習が簡単
   - 標準パッケージの知識がそのまま使える
   - シンプルな機能で分かりやすい

2. 柔軟なルーティング
   - URLに変数を含められる
   - HTTPメソッドで分岐できる

```go
// gorilla/muxの例
r := mux.NewRouter()

// URLに変数を含む例（/api/todos/1 など）
r.HandleFunc("/api/todos/{id}", getTodo).Methods("GET")

// 複数のHTTPメソッドを設定
r.HandleFunc("/api/todos", handler).
    Methods("GET", "POST")
```

### gin
高機能で高速なWebフレームワーク

#### メリット
1. 便利な機能が豊富
   - バリデーション（データチェック）
   - ミドルウェア（共通処理）
   - エラーハンドリング

2. 高いパフォーマンス
   - 処理が速い
   - メモリ使用量が少ない

```go
// ginの例
r := gin.Default()

// シンプルなルーティング
r.GET("/api/todos/:id", getTodo)

// バリデーション機能の例
type Todo struct {
    Title string `binding:"required,min=1,max=100"`
    Done  bool
}
```

### どちらを選ぶべき？

1. 小規模なプロジェクト
   → gorilla/mux
   - シンプルで学習コストが低い
   - 標準パッケージの勉強になる

2. 大規模なプロジェクト
   → gin
   - 便利な機能が揃っている
   - パフォーマンスが重要な場合

参考文献: 
-『Goで作るはじめてのWebアプリケーション改訂版』技術評論社
- https://techbookfest.org/product/jXDAEU1dR53kbZkgtDm9zx