mirror of
https://gitea.com/gitea/docs.git
synced 2026-06-29 15:44:37 +00:00
Signed-off-by: appleboy <appleboy.tw@gmail.com> Reviewed-on: https://gitea.com/gitea/docs/pulls/197 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
118 lines
6.0 KiB
Markdown
118 lines
6.0 KiB
Markdown
---
|
||
date: "2021-11-01T23:41:00+08:00"
|
||
slug: "guidelines-backend"
|
||
sidebar_position: 20
|
||
aliases:
|
||
- /zh-tw/guidelines-backend
|
||
---
|
||
|
||
# 後端開發指南
|
||
|
||
## 背景
|
||
|
||
Gitea 使用 Golang 作為後端程式語言。它使用了許多第三方套件,也自己寫了一些。
|
||
例如,Gitea 使用 [Chi](https://github.com/go-chi/chi) 作為基本的網頁框架。[Xorm](https://xorm.io) 是一個 ORM 框架,用來與資料庫互動。
|
||
因此,管理這些套件非常重要。在開始寫後端程式碼之前,請遵循以下指南。
|
||
|
||
## 套件設計指南
|
||
|
||
### 套件列表
|
||
|
||
為了保持程式碼的可理解性並避免循環依賴,擁有良好的程式碼結構非常重要。Gitea 的後端分為以下幾個部分:
|
||
|
||
- `build`: 幫助構建 Gitea 的腳本。
|
||
- `cmd`: 所有 Gitea 的實際子命令,包括 web、doctor、serv、hooks、admin 等等。`web` 會啟動網頁服務。`serv` 和 `hooks` 會被 Git 或 OpenSSH 調用。其他子命令可以幫助維護 Gitea。
|
||
- `tests`: 常見的測試工具函數
|
||
- `tests/integration`: 整合測試,用來測試後端的回歸
|
||
- `tests/e2e`: 端到端測試,用來測試前端和後端的相容性和視覺回歸。
|
||
- `models`: 包含由 xorm 用來構建資料庫表格的資料結構。它也包含查詢和更新資料庫的函數。應避免依賴其他 Gitea 程式碼。可以在某些情況下例外,例如記錄。
|
||
- `models/db`: 基本的資料庫操作。所有其他 `models/xxx` 套件應依賴此套件。`GetEngine` 函數應僅從 `models/` 調用。
|
||
- `models/fixtures`: 單元測試和整合測試中使用的樣本資料。一個 `yml` 文件代表一個表格,測試開始時會將其載入資料庫。
|
||
- `models/migrations`: 存儲版本之間的資料庫遷移。更改資料庫結構的 PR **必須** 也有遷移步驟。
|
||
- `modules`: 處理 Gitea 中特定功能的不同模組。進行中:其中一些應移動到 `services`,特別是那些依賴於 models 的,因為它們依賴於資料庫。
|
||
- `modules/setting`: 存儲從 ini 文件讀取的所有系統配置,並已被各處引用。但應盡可能作為函數參數使用。
|
||
- `modules/git`: 與 `Git` 命令行或 Gogit 套件互動的套件。
|
||
- `public`: 編譯後的前端文件(javascript、圖片、css 等)。
|
||
- `routers`: 處理伺服器請求。由於它使用其他 Gitea 套件來處理請求,其他套件(models、modules 或 services)不得依賴 routers。
|
||
- `routers/api` 包含處理 RESTful API 請求的 `/api/v1` 路由。
|
||
- `routers/install` 僅在系統處於安裝模式(INSTALL_LOCK=false)時響應。
|
||
- `routers/private` 只會被內部子命令調用,特別是 `serv` 和 `hooks`。
|
||
- `routers/web` 會處理來自網頁瀏覽器或 Git SMART HTTP 協議的 HTTP 請求。
|
||
- `services`: 支持常見路由操作或命令執行的函數。使用 `models` 和 `modules` 來處理請求。
|
||
- `templates`: 用於生成 html 輸出的 Golang 模板。
|
||
|
||
### 套件依賴
|
||
|
||
由於 Golang 不支持導入循環,我們必須仔細決定套件依賴關係。這些套件之間有一些層次。以下是理想的套件依賴方向。
|
||
|
||
`cmd` -> `routers` -> `services` -> `models` -> `modules`
|
||
|
||
從左到右,左邊的套件可以依賴右邊的套件,但右邊的套件不得依賴左邊的套件。同一層次的子套件可以根據該層次的規則依賴。
|
||
|
||
:::warning
|
||
為什麼我們需要在 `models` 之外的資料庫交易?以及如何實現?
|
||
某些操作應允許在資料庫記錄插入/更新/刪除失敗時回滾。
|
||
因此,services 必須允許創建資料庫交易。這裡有一些例子,
|
||
|
||
```go
|
||
// services/repository/repository.go
|
||
func CreateXXXX() error {
|
||
return db.WithTx(func(ctx context.Context) error {
|
||
// 做一些事情,如果返回錯誤,它會自動回滾
|
||
if err := issues.UpdateIssue(ctx, repoID); err != nil {
|
||
// ...
|
||
return err
|
||
}
|
||
// ...
|
||
return nil
|
||
})
|
||
}
|
||
```
|
||
|
||
你不應該在 `services` 中直接使用 `db.GetEngine(ctx)`,而是應該在 `models/` 下寫一個函數。
|
||
如果該函數將在交易中使用,只需將 `context.Context` 作為該函數的第一個參數。
|
||
|
||
```go
|
||
// models/issues/issue.go
|
||
func UpdateIssue(ctx context.Context, repoID int64) error {
|
||
e := db.GetEngine(ctx)
|
||
|
||
// ...
|
||
}
|
||
```
|
||
|
||
:::
|
||
|
||
### 套件名稱
|
||
|
||
對於頂層套件,使用複數作為套件名稱,即 `services`、`models`,對於子套件,使用單數,
|
||
即 `services/user`、`models/repository`。
|
||
|
||
### 導入別名
|
||
|
||
由於有些套件使用相同的套件名稱,可能會發現像 `modules/user`、`models/user` 和 `services/user` 這樣的套件。
|
||
當這些套件在一個 Go 文件中導入時,很難知道我們使用的是哪個套件,以及它是變量名還是導入名。因此,我們總是建議使用導入別名。為了區分常見的駝峰式變量名,只需使用 **snake_case** 作為導入別名。
|
||
即 `import user_service "code.gitea.io/gitea/services/user"`
|
||
|
||
### 實現 `io.Closer`
|
||
|
||
如果一種類型實現了 `io.Closer`,多次調用 `Close` 不應失敗或 `panic`,而是返回錯誤或 `nil`。
|
||
|
||
### 重要注意事項
|
||
|
||
- 永遠不要在沒有明確 `WHERE` 子句的情況下寫 `x.Update(exemplar)`:
|
||
- 這會導致表中的所有行都被更新為 exemplar 的非零值 - 包括 ID。
|
||
- 你通常應該寫 `x.ID(id).Update(exemplar)`。
|
||
- 如果在遷移期間你正在插入一個預設 ID 的表格,使用 `x.Insert(exemplar)`:
|
||
- 對於 MSSQL 變體,你需要 `` SET IDENTITY_INSERT `table` ON ``(否則遷移會失敗)
|
||
- 但是,你還需要更新 postgres 的 id 序列 - 遷移會在這裡默默通過,但後來的插入會失敗:
|
||
`` SELECT setval('table_name_id_seq', COALESCE((SELECT MAX(id)+1 FROM `table_name`), 1), false) ``
|
||
|
||
### 未來任務
|
||
|
||
目前,我們正在進行一些重構,以完成以下事情:
|
||
|
||
- 修正不符合規則的代碼。
|
||
- `models` 中的文件太多了,所以我們正在將其中一些移動到子套件 `models/xxx`。
|
||
- 一些 `modules` 子套件應移動到 `services`,因為它們依賴於 `models`。
|