Go + Gin API 實作:Clean Architecture、JWT、Redis、交易管理


2025年9月24日
程式語言

Go + Gin API 實作:Clean Architecture、JWT、Redis、交易管理


Go + Gin API 實作經驗分享,涵蓋 Clean Architecture 分層設計、CRUD、JWT、Redis 快取、交易管理,並比較 C# 與 Go 的開發差異、Swagger 文件管理與常見開發坑,適合後端工程師參考。

前言

🔗

平常主要使用 C#、Node.js 做後端開發,最近發現 Go 職缺數有明顯上升,而產業橫跨金融科技、Web3等,實用場景很多,也都是大流量、賺錢的產業,查詢了一下原因不管是在微服務、高併發情況下,使用 Go 都能有更好的效能,與簡單簡潔的實作語法。於是興起了學習 Go 的念頭。

維持以往的習慣,在接觸一個新語言或是框架,都會建置一個範例專案,好讓後續若有需求需要從零開始建置,能快速實作各種功能,以利開發進度。

所以此文章以 go 與 gin 建立一個基本的後端 API 範本,使用基本分層結構、實作 CRUD、JWT 權限驗證、Redis 快取(斷線可重連)與交易等基本的業界常見基礎功能。

GitHub 專案原始碼

專案架構

🔗

採用分層架構,將系統分為四層。Handler 處理 HTTP 請求、Service 負責業務邏輯、Repository 專門與資料庫交互、Model 定義資料結構。

這樣可以讓程式碼更容易維護、測試,也能讓職責清晰分工。相信對於熟悉 C# 或是使用過 Nest.js 框架就能清楚將程式切成處理請求、跑商業邏輯、存取資料、定義資料等邏輯是非常容易能轉換。

遇到的坑

🔗

Swagger + Model alias 衝突

🔗

在用 Go 搭配 Swagger (swaggo/swag) 生文件的時候,一開始還真的有點不習慣,因為 API 的註解要直接寫在 Handler function 上方,連路由也是在這裡定義。設定回傳或接收格式時雖然可以直接引用已經定義好的物件,但這些物件通常來自不同的 model 套件。

問題就出在這裡:若是要同時引入 user/modelorder/model 裡的 struct,在 Go 裡我可以靠 import 時,使用 alias (userModel.User, orderModel.User) 來區分,但 Swagger 產文件時完全不看 alias,只認結構體的名字,結果就衝突了。

我目前的解法是把可能共用的 struct 集中放在額外建立的 commonmodel 資料夾,這樣在引入模組時就可以有效區分model 與 commonmodel,這樣就不會衝突了。

社群上的建議是幫 struct 加上 //@name,給它一個 Swagger 專用的名字(例如 UserModelOrderUserModel),這樣文件就能區分。但因為我這邊的業務結構還不算太複雜,先用 common 的方式就夠了,所以還沒有特別去試 //@name

這樣至少能先確保 Swagger 文件乾淨、不衝突。

值與指標

🔗

在轉換到 Go 的過程中,值(*)與指標(&) 是最需要重新適應的地方。

在 C#、Python、Node.js 這些語言裡,大部分物件預設就是參考型別,修改屬性通常會直接作用在同一份資料上,很少需要去思考「我要傳值還是傳參考」。

Go 則不同,struct 是值型別,傳遞或呼叫方法時會複製一份資料。如果要修改原始內容,就必須透過指標 (*T)。這也直接影響到 Method Receiver 的設計:

  • Value Receiver:呼叫時會複製一份資料,方法內的修改不會影響到原始物件。
  • Pointer Receiver:傳遞的是參考,方法內的修改會直接作用在原始物件。

對剛從 C# 或動態語言轉到 Go 的人來說,這會是第一個常見的坑。因為在其他語言裡幾乎不用管這些細節,但在 Go 裡,你得清楚決定「我要不要讓這個方法改到原本的資料」,這也是 Go 在設計上強調「顯式與清晰」的地方。

值(*)與指標(&)細節可參考:
➡️ Go Pointers Explained: A Beginner’s Guide to & and *

交易 (Transaction) 與 Clean Architecture

🔗

在專案裡我希望能盡量維持 Clean Architecture 的風格:

  • Repository:單純負責 DB CRUD。
  • Service:專心處理業務邏輯。

但當業務流程需要多個 Repository 協同操作(例如建立使用者的同時寫入訂單),就會遇到 交易該在哪裡實作 的問題。

如果讓每個 Repository 自己去管交易,後續勢必會很混亂,因為交易邏輯會分散各處,不好維護也難以測試。

因此我選擇 額外實作 Unit of Work,並在 Service 層導入,把多個 Repository 操作統一包進一個 transaction 裡。這樣既能確保資料一致性,也能維持職責清晰:Repository 專注 CRUD,Service 掌握交易範圍,整體架構乾淨、可控又符合 Clean Architecture。

後續規劃

🔗

原本也想在這個專案裡實作 高併發、並行處理、Message Queue 等進階內容,但後來覺得專案會變得過於複雜,反而在查詢或參考範例時容易搞混核心邏輯。

所以決定進階主題留到後續其他專案中再實踐,讓每個專案專注於一個主題,這樣對於學習與維護還有範例查詢應該會更簡單。

結論

🔗

相較於 C# 或 Nest.js 這類有完整框架支撐的生態,Go 少了許多「現成工具」可用。

  • 優點:靈活度高、效能佳。

  • 缺點:需要自己定義很多結構,如果沒規劃好,專案就很容易亂掉。

不過 Go 有一個超大的亮點:啟動速度快到飛起,非常適合微服務這種小而專的場景。服務單一、結構簡單,就能輕量又高效地運行。這是我實作後最有感的一點。

老實說,一開始我對 Go 並沒有太大興趣,覺得「沒有全家桶式的框架,更多時候需要工程師自己組合生態裡的套件,這帶來靈活,但底子不夠很容易亂,好麻煩」。但看到 Go 那隻吉祥物,心想「好可愛,不得不來學一下!🫠」

Go系統設計Web



Avatar

Alvin

軟體工程師,討厭瞎忙,喜歡用邏輯解決問題,努力在盲目追求成就感與放鬆的生活中取得平衡。

相關文章