Like Share Discussion Bookmark Smile

J.J. Huang   2025-06-24   Getting Started Golang 07.套件   瀏覽次數:次   DMCA.com Protection Status

Go | 搭配套件架構實戰範例

💬 簡介

在實務開發中,良好的套件分層架構是建立可維護專案的基石。
不論是單純的 CRUD API,或是多模組系統,分層設計有助於降低耦合、提升模組清晰度與測試友善性。

本篇將透過一個簡易的「任務管理 API 專案」為例,示範如何以分層方式規劃並實作 Go 套件架構。

圖片來源:Gophers


🧱 專案目錄架構設計

1
2
3
4
5
6
7
8
9
10
/task-api/
|- cmd/ // 程式進入點(main)
|- internal/
|- handler/ // HTTP 控制器層
|- service/ // 商業邏輯層
|- repository/ // 資料存取層
|- model/ // 資料結構與 DTO
|- db/ // 資料庫初始化與連線
|- pkg/ // 可共用的通用套件(如 logger、config)
|- go.mod
  • internal/ 用於專案內部模組,禁止外部 import
  • pkg/ 放置具重用性的小工具或第三方封裝

📦 各層功能與實作

🧩 Model 層(model/task.go)

1
2
3
4
5
6
7
package model

type Task struct {
ID int `json:"id"`
Title string `json:"title"`
Status string `json:"status"`
}

💾 Repository 層(repository/task_repo.go)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package repository

import (
"database/sql"
"task-api/internal/model"
)

type TaskRepository interface {
GetAll() ([]model.Task, error)
Create(task model.Task) error
}

type taskRepo struct {
db *sql.DB
}

func NewTaskRepo(db *sql.DB) TaskRepository {
return &taskRepo{db: db}
}

func (r *taskRepo) GetAll() ([]model.Task, error) {
rows, err := r.db.Query("SELECT id, title, status FROM tasks")
// ...
}

func (r *taskRepo) Create(task model.Task) error {
_, err := r.db.Exec("INSERT INTO tasks (title, status) VALUES (?, ?)", task.Title, task.Status)
// ...
}

🧠 Service 層(service/task_service.go)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package service

import (
"task-api/internal/model"
"task-api/internal/repository"
)

type TaskService interface {
ListTasks() ([]model.Task, error)
AddTask(title string) error
}

type taskService struct {
repo repository.TaskRepository
}

func NewTaskService(r repository.TaskRepository) TaskService {
return &taskService{repo: r}
}

func (s *taskService) ListTasks() ([]model.Task, error) {
return s.repo.GetAll()
}

func (s *taskService) AddTask(title string) error {
task := model.Task{Title: title, Status: "pending"}
return s.repo.Create(task)
}

🌐 Handler 層(handler/task_handler.go)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package handler

import (
"encoding/json"
"net/http"
"task-api/internal/service"
)

type TaskHandler struct {
svc service.TaskService
}

func NewTaskHandler(svc service.TaskService) *TaskHandler {
return &TaskHandler{svc: svc}
}

func (h *TaskHandler) GetTasks(w http.ResponseWriter, r *http.Request) {
tasks, _ := h.svc.ListTasks()
json.NewEncoder(w).Encode(tasks)
}

func (h *TaskHandler) PostTask(w http.ResponseWriter, r *http.Request) {
var body struct {
Title string `json:"title"`
}
json.NewDecoder(r.Body).Decode(&body)
h.svc.AddTask(body.Title)
w.WriteHeader(http.StatusCreated)
}

🚀 main 進入點(cmd/main.go)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"database/sql"
"net/http"
"task-api/internal/db"
"task-api/internal/handler"
"task-api/internal/repository"
"task-api/internal/service"

_ "github.com/mattn/go-sqlite3"
)

func main() {
conn := db.NewConnection() // sqlite 或其他初始化
repo := repository.NewTaskRepo(conn)
svc := service.NewTaskService(repo)
h := handler.NewTaskHandler(svc)

http.HandleFunc("/tasks", h.GetTasks)
http.HandleFunc("/tasks/create", h.PostTask)
http.ListenAndServe(":8080", nil)
}

🛠 資料庫初始化(db/sqlite.go)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package db

import (
"database/sql"
"log"
)

func NewConnection() *sql.DB {
db, err := sql.Open("sqlite3", "tasks.db")
if err != nil {
log.Fatal(err)
}
return db
}

📌 小結與實務建議

✅ 分層設計的好處:

好處 說明
職責清晰 各層只關注單一職責(SRP)
易於測試 每層皆可獨立 Mock 與單元測試
降低耦合 藉由介面隔離各層
擴充容易 更換儲存層(ex: 改用 PostgreSQL)幾乎不動邏輯

⚠️ 常見陷阱:

  • 所有邏輯塞在 handler 或 main 裡(無法測試與重構)
  • 資料庫細節散落整個專案中
  • 各層之間無明確界線或過度互相呼叫

🎯 總結

透過中小型專案實作,你可以體會套件分層的實質好處:

  • ✅ 每層聚焦單一責任
  • ✅ 強化可測試性與可重用性
  • ✅ 架構清晰,可因應未來擴展

分層設計不僅是一種技術技巧,更是維護專案健康長壽的重要基石。

最後建議回顧一下 Go | 菜鳥教學 目錄,了解其章節內容。


註:以上參考了
Go