主要なアーキテクチャパターン
1. MVCアーキテクチャ
-
特徴
- Model(データ)、View(表示)、Controller(制御)の3層構造
-
メリット
- 責務が明確に分離され、保守性が高い
- コードの再利用性が向上
- テストが容易
-
デメリット
- Model、Controllerが肥大化しやすい
2. レイヤードアーキテクチャ
-
特徴
- プレゼンテーション層、ビジネスロジック層、データアクセス層などの階層構造
-
メリット:
- 関心事の分離が明確
- 各層の独立した開発が可能
- 保守性とスケーラビリティが高い
-
デメリット
- 層間の依存関係管理が複雑になる可能性
- パフォーマンスへの影響(層間通信のオーバーヘッド)
3. クリーンアーキテクチャ
-
特徴
- ドメイン中心の設計、依存関係の方向が内側に向く
-
メリット
- ビジネスロジックが外部要因から保護される
- テスタビリティが高い
- フレームワークに依存しない設計
-
デメリット
- 学習曲線が急
- 小規模プロジェクトでは複雑すぎる可能性
実装例
MVCアーキテクチャの実装例
model/user.go
package model
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
view/user_view.go
package view
import (
"encoding/json"
"go-cs/model"
)
type UserView struct {
User *model.User
}
func (v *UserView) RenderJSON() ([]byte, error) {
return json.Marshal(v.User)
}
controller/user_controller.go
package controller
import (
"database/sql"
"encoding/json"
"net/http"
"strconv"
"go-cs/service"
)
type UserController struct {
userService *service.UserService
}
func NewUserController(userService *service.UserService) *UserController {
return &UserController{userService: userService}
}
func (c *UserController) GetUser(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
http.Error(w, "Invalid user ID", http.StatusBadRequest)
return
}
user, err := c.userService.GetUser(id)
if err != nil {
if err == sql.ErrNoRows {
http.Error(w, "User not found", http.StatusNotFound)
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
service/user_service.go
package service
import (
"database/sql"
"go-cs/model"
)
type UserService struct {
db *sql.DB
}
func NewUserService(db *sql.DB) *UserService {
return &UserService{db: db}
}
func (s *UserService) GetUser(id int) (*model.User, error) {
user := &model.User{}
err := s.db.QueryRow("SELECT id, name, email FROM users WHERE id = ?", id).
Scan(&user.ID, &user.Name, &user.Email)
if err != nil {
return nil, err
}
return user, nil
}
main.go
package main
import (
"database/sql"
"log"
"net/http"
"go-cs/controller"
"go-cs/service"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, err := sql.Open("sqlite3", "./users.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// テーブル作成
createTable(db)
// サンプルデータ挿入
insertSampleData(db)
// 依存関係の注入
userService := service.NewUserService(db)
userController := controller.NewUserController(userService)
// ルーティング
http.HandleFunc("GET /users/{id}", userController.GetUser)
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func createTable(db *sql.DB) {
query := `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);`
_, err := db.Exec(query)
if err != nil {
log.Fatal(err)
}
}
func insertSampleData(db *sql.DB) {
_, err := db.Exec(`INSERT INTO users (name, email) VALUES (?, ?)`,
"Tanaka Taro", "taro@example.com")
if err != nil {
log.Fatal(err)
}
}
curl http://localhost:8080/users/1
{"id":1,"name":"Tanaka Taro","email":"taro@example.com"}
curl http://localhost:8080/users/123
User not found