Series Go Backend: Quản lý module và package trong Go (go mod)
Trong bài trước, chúng ta đã khởi tạo dự án với go mod init. Hôm nay, chúng ta sẽ đi sâu hơn vào cách Go tổ chức mã nguồn. Hiểu rõ hệ thống Module và Package là nền tảng bắt buộc để bạn có thể cấu trúc các dự án backend lớn một cách khoa học và gọn gàng.
1. Phân biệt Module và Package
Trong Go, hai khái niệm này thường đi liền với nhau nhưng mang ý nghĩa khác biệt:
-
Package (Gói): Là một thư mục chứa một hoặc nhiều file
.go. Các file trong cùng một thư mục phải khai báo cùng tên package ở dòng đầu tiên (ngoại trừ file test). Package giúp nhóm các đoạn code có chung chức năng lại với nhau. -
Module (Mô-đun): Là một tập hợp các package được phát hành, định tuyến và phiên bản hóa cùng nhau. Module được định nghĩa bởi file
go.modnằm ở thư mục gốc.
Bạn có thể hiểu đơn giản: Module giống như cả một project (quản lý qua go.mod), còn Package giống như các thư mục con chứa logic bên trong project đó.
2. Làm việc với Go Modules (go.mod và go.sum)
Khi bạn làm backend, việc kéo các thư viện bên ngoài (như framework REST API, driver kết nối database, redis client) là không thể tránh khỏi.
Thêm một thư viện bên ngoài (Dependency) Để cài đặt một thư viện, bạn sử dụng lệnh go get. Ví dụ, cài đặt framework Gin để làm web server:
Bash
go get -u github.com/gin-gonic/gin
(Tham số -u dùng để update thư viện lên phiên bản minor/patch mới nhất).
Dọn dẹp Dependencies Trong quá trình code, bạn có thể import thử một thư viện nhưng sau đó không dùng nữa, hoặc copy code từ đâu đó về mà chưa chạy go get. Lệnh cứu cánh của bạn là:
Bash
go mod tidy
Lệnh này cực kỳ quan trọng. Nó sẽ tự động quét toàn bộ source code của bạn:
-
Tải về những thư viện bạn đã
importnhưng chưa có trong máy. -
Xóa bỏ khỏi
go.modnhững thư viện bạn không còn sử dụng trong code.
File go.sum là gì? Khi bạn chạy go get hoặc go mod tidy, Go sẽ tự động tạo/cập nhật file go.sum. File này chứa mã băm (checksum) của các thư viện bạn tải về. Mục đích của nó là đảm bảo tính toàn vẹn: nếu lần sau bạn (hoặc CI/CD server) tải lại thư viện đó, mã băm phải khớp, giúp tránh tình trạng code thư viện bị ai đó can thiệp và sửa đổi lén lút trên GitHub.
3. Tạo và sử dụng Custom Package nội bộ
Hãy thử tạo một package nội bộ để xem cách Go tổ chức code. Giả sử dự án của bạn có cấu trúc như sau:
Plaintext
go-hello-world/
├── go.mod
├── main.go
└── mathutil/
└── calc.go
Tạo file mathutil/calc.go và định nghĩa logic:
Go
package mathutil
// Tên hàm viết hoa chữ cái đầu: Public (Exported)
// Các package khác CÓ THỂ gọi được hàm này
func Add(a int, b int) int {
return a + b
}
// Tên hàm viết thường chữ cái đầu: Private (Unexported)
// Các package khác KHÔNG THỂ gọi được hàm này, chỉ dùng nội bộ trong package mathutil
func multiply(a int, b int) int {
return a * b
}
Quy tắc "Vàng" về Tầm vực (Visibility) trong Go: Go không dùng các từ khóa như public, private, hay protected như C++ hay PHP. Thay vào đó, Go quy định tầm vực bằng chữ cái đầu tiên:
-
Viết HOA chữ cái đầu (VD:
Add,User,ConnectDB): Exported (Public), truy cập được từ bên ngoài package. -
Viết thường chữ cái đầu (VD:
multiply,user,config): Unexported (Private), chỉ nội bộ các file trong cùng package đó mới gọi được nhau.
Bây giờ, quay lại main.go để sử dụng package mathutil vừa tạo:
Go
package main
import (
"fmt"
// Import package nội bộ bằng: Tên_Module/đường_dẫn_thư_mục
"github.com/hieudev/go-hello-world/mathutil"
)
func main() {
sum := mathutil.Add(5, 10)
fmt.Printf("Tổng là: %d\n", sum)
// Nếu bạn gọi mathutil.multiply(5, 10) ở đây, trình biên dịch sẽ báo lỗi!
}
4. Hàm init()
Trong một package, bạn có thể khai báo một (hoặc nhiều) hàm đặc biệt tên là init(). Hàm này không cần nhận tham số và không trả về gì cả. Nó sẽ được Go tự động chạy một lần duy nhất ngay khi package đó được import, trước cả khi hàm main() bắt đầu.
Go
package mathutil
import "fmt"
func init() {
fmt.Println("Package mathutil đang được khởi tạo!")
// Thường dùng để khởi tạo giá trị mặc định, load config, hoặc đăng ký driver database
}
Nắm vững cách quản lý module và quy ước Exported/Unexported của package sẽ giúp code backend của bạn sau này bảo mật và tuân thủ nguyên tắc đóng gói (Encapsulation) cực kỳ chặt chẽ.
All rights reserved