☰ Mục lục series
Tài liệu Go tiếng Việt
  1. Bắt đầu (Getting started)
    1. Tổng quan
    2. Tải về và cài đặt
    3. Bắt đầu với Go
    4. Quản lý các bản cài đặt Go
    5. Plugin trình soạn thảo và IDE
  2. Dạo quanh Go (A Tour of Go)
    1. Chào mừng
    2. Package, biến và hàm
    3. Điều khiển luồng
    4. Struct, slice và map
    5. Method và interface
    6. Generics
    7. Xử lý đồng thời
  3. Hướng dẫn (Tutorials)
    1. Danh sách hướng dẫn
    2. Tạo một Go module
    3. Gọi mã của bạn từ một module khác
    4. Trả về và xử lý lỗi
    5. Trả về lời chào ngẫu nhiên
    6. Trả về lời chào cho nhiều người
    7. Thêm một test
    8. Biên dịch và cài đặt ứng dụng
    9. Kết luận
    10. Bắt đầu với workspace nhiều module
    11. Phát triển RESTful API với Go và Gin
    12. Truy cập cơ sở dữ liệu quan hệ
    13. Bắt đầu với generics
    14. Bắt đầu với fuzzing
    15. Làm việc với JSON
    16. Tìm và sửa các dependency có lỗ hổng với govulncheck
    17. Tìm và khắc phục phụ thuộc có lỗ hổng với VS Code Go
    18. Viết ứng dụng web
  4. Viết code Go
    1. Cách viết mã Go
    2. Effective Go
    3. Doc Comment trong Go
  5. Module
    1. Phát triển và publish module
    2. Quản lý phụ thuộc
    3. Tổ chức một module Go
    4. Quản lý mã nguồn của module
    5. Cách đánh số phiên bản module
    6. Quy trình phát hành và đánh phiên bản module
    7. Publish một module
    8. Phát triển một bản cập nhật major version
    9. Tham chiếu tệp go.mod
    10. Ngừng hỗ trợ 'go get' cho việc cài đặt tệp thực thi
  6. Cơ sở dữ liệu (Database)
    1. Truy cập cơ sở dữ liệu quan hệ
    2. Mở một handle cơ sở dữ liệu
    3. Thực thi câu lệnh SQL không trả về dữ liệu
    4. Truy vấn dữ liệu
    5. Dùng prepared statement
    6. Thực thi transaction
    7. Huỷ các thao tác đang chạy
    8. Quản lý kết nối
    9. Tránh rủi ro SQL injection
  7. Chẩn đoán và hiệu năng
    1. Chẩn đoán
    2. Hướng dẫn về Go Garbage Collector
    3. Tối ưu hoá dựa trên profile
    4. Data Race Detector
    5. Gỡ lỗi mã Go bằng GDB
    6. Hỗ trợ coverage profiling cho integration test
  8. Bảo mật (Security)
    1. Tổng quan bảo mật
    2. Thực hành tốt về bảo mật cho lập trình viên Go
    3. Quản lý lỗ hổng trong Go
    4. Cơ sở dữ liệu lỗ hổng của Go
    5. Quét lỗ hổng trong IDE
    6. Chính sách CNA của Go
    7. Go Fuzzing
    8. Chi tiết kỹ thuật của Go Fuzzing
    9. Chính sách bảo mật của Go
    10. Threat Model của Go
    11. Các quyết định về bảo mật của Go
    12. Tuân thủ FIPS 140-3
  9. Công cụ và toolchain
    1. Tài liệu về các lệnh
    2. Về lệnh go
    3. Go Toolchains
    4. Telemetry của Go
    5. Cài đặt Go từ mã nguồn
    6. Cài đặt và sử dụng gccgo
  10. Tham khảo
    1. Câu hỏi thường gặp (FAQ)
    2. Go 1 và tương lai của các chương trình Go
    3. Hướng dẫn chuyển đổi sang encoding/json/v2
    4. Hướng dẫn đóng góp
    5. Đóng góp cho frontend gccgo
  11. Phụ lục
    1. Bảng thuật ngữ
Module

Tổ chức một module Go

Bản dịch từ go.dev/doc/modules/layout

Một câu hỏi thường gặp của những người mới đến với Go là “Tôi nên tổ chức dự án Go của mình thế nào?”, xét về bố cục các tệp và thư mục. Mục tiêu của tài liệu này là đưa ra một số hướng dẫn giúp trả lời câu hỏi đó. Để tận dụng tối đa tài liệu này, hãy bảo đảm bạn đã nắm những kiến thức cơ bản về module Go bằng cách đọc hướng dẫn và quản lý mã nguồn module.

Dự án Go có thể bao gồm các package, các chương trình dòng lệnh, hoặc kết hợp cả hai. Hướng dẫn này được sắp xếp theo loại dự án.

Package cơ bản

Một package Go cơ bản có toàn bộ mã nằm trong thư mục gốc của dự án. Dự án gồm một module duy nhất, và module đó gồm một package duy nhất. Tên package trùng với thành phần đường dẫn cuối cùng của tên module. Với một package rất đơn giản chỉ cần một tệp Go, cấu trúc dự án là:

project-root-directory/
  go.mod
  modname.go
  modname_test.go

[xuyên suốt tài liệu này, tên tệp/tên package hoàn toàn là tuỳ ý]

Giả sử thư mục này được tải lên một repository GitHub tại github.com/someuser/modname, dòng module trong tệp go.mod phải ghi là module github.com/someuser/modname.

Mã trong modname.go khai báo package bằng:

package modname

// ... package code here

Người dùng sau đó có thể dựa vào package này bằng cách import nó trong mã Go của họ với:

import "github.com/someuser/modname"

Một package Go có thể được chia thành nhiều tệp, tất cả nằm trong cùng một thư mục, ví dụ:

project-root-directory/
  go.mod
  modname.go
  modname_test.go
  auth.go
  auth_test.go
  hash.go
  hash_test.go

Tất cả các tệp trong thư mục đều khai báo package modname.

Lệnh cơ bản

Một chương trình thực thi cơ bản (hay công cụ dòng lệnh) được cấu trúc tuỳ theo mức độ phức tạp và kích thước mã của nó. Chương trình đơn giản nhất có thể chỉ gồm một tệp Go duy nhất nơi func main được định nghĩa. Các chương trình lớn hơn có thể chia mã ra nhiều tệp, tất cả đều khai báo package main:

project-root-directory/
  go.mod
  auth.go
  auth_test.go
  client.go
  main.go

Ở đây tệp main.go chứa func main, nhưng đó chỉ là quy ước. Tệp “main” cũng có thể được đặt tên modname.go (với một giá trị modname phù hợp) hoặc bất kỳ tên nào khác.

Giả sử thư mục này được tải lên một repository GitHub tại github.com/someuser/modname, dòng module trong tệp go.mod phải ghi là:

module github.com/someuser/modname

Và người dùng có thể cài đặt nó trên máy của họ bằng:

$ go install github.com/someuser/modname@latest

Package hoặc lệnh kèm các package hỗ trợ

Các package hoặc lệnh lớn hơn có thể hưởng lợi từ việc tách một phần chức năng ra thành các package hỗ trợ. Ban đầu, nên đặt các package như vậy vào một thư mục tên là internal; điều này ngăn các module khác phụ thuộc vào những package mà chúng tôi không nhất thiết muốn phơi bày và hỗ trợ cho việc dùng từ bên ngoài. Vì các dự án khác không thể import mã từ thư mục internal của chúng ta, chúng ta có thể tự do tái cấu trúc API của nó và nói chung là sắp xếp lại mọi thứ mà không làm hỏng người dùng bên ngoài. Do đó, cấu trúc dự án cho một package là:

project-root-directory/
  internal/
    auth/
      auth.go
      auth_test.go
    hash/
      hash.go
      hash_test.go
  go.mod
  modname.go
  modname_test.go

Tệp modname.go khai báo package modname, auth.go khai báo package auth và cứ thế. modname.go có thể import package auth như sau:

import "github.com/someuser/modname/internal/auth"

Bố cục cho một lệnh kèm các package hỗ trợ trong thư mục internal rất tương tự, chỉ khác là (các) tệp trong thư mục gốc khai báo package main.

Nhiều package

Một module có thể gồm nhiều package có thể import được; mỗi package có thư mục riêng, và có thể được tổ chức phân cấp. Đây là một cấu trúc dự án mẫu:

project-root-directory/
  go.mod
  modname.go
  modname_test.go
  auth/
    auth.go
    auth_test.go
    token/
      token.go
      token_test.go
  hash/
    hash.go
  internal/
    trace/
      trace.go

Xin nhắc lại, chúng ta giả định rằng dòng module trong go.mod ghi là:

module github.com/someuser/modname

Package modname nằm ở thư mục gốc, khai báo package modname và có thể được người dùng import bằng:

import "github.com/someuser/modname"

Các package con có thể được người dùng import như sau:

import "github.com/someuser/modname/auth"
import "github.com/someuser/modname/auth/token"
import "github.com/someuser/modname/hash"

Package trace nằm trong internal/trace không thể được import bên ngoài module này. Chúng tôi khuyến nghị giữ các package trong internal nhiều nhất có thể.

Nhiều lệnh

Nhiều chương trình trong cùng một repository thường sẽ có các thư mục riêng biệt:

project-root-directory/
  go.mod
  internal/
    ... shared internal packages
  prog1/
    main.go
  prog2/
    main.go

Trong mỗi thư mục, các tệp Go của chương trình khai báo package main. Một thư mục internal ở cấp cao nhất có thể chứa các package dùng chung cho tất cả các lệnh trong repository.

Người dùng có thể cài đặt những chương trình này như sau:

$ go install github.com/someuser/modname/prog1@latest
$ go install github.com/someuser/modname/prog2@latest

Một quy ước phổ biến là đặt tất cả các lệnh trong một repository vào thư mục cmd; dù điều này không thật sự cần thiết trong một repository chỉ gồm các lệnh, nó rất hữu ích trong một repository hỗn hợp có cả lệnh lẫn các package có thể import được, như chúng tôi sẽ bàn tiếp theo.

Package và lệnh trong cùng một repository

Đôi khi một repository sẽ cung cấp cả các package có thể import được lẫn các lệnh có thể cài đặt với chức năng liên quan tới nhau. Đây là một cấu trúc dự án mẫu cho repository như vậy:

project-root-directory/
  go.mod
  modname.go
  modname_test.go
  auth/
    auth.go
    auth_test.go
  internal/
    ... internal packages
  cmd/
    prog1/
      main.go
    prog2/
      main.go

Giả sử module này tên là github.com/someuser/modname, người dùng giờ đây vừa có thể import package từ nó:

import "github.com/someuser/modname"
import "github.com/someuser/modname/auth"

Vừa có thể cài đặt chương trình từ nó:

$ go install github.com/someuser/modname/cmd/prog1@latest
$ go install github.com/someuser/modname/cmd/prog2@latest

Dự án server

Go là một lựa chọn ngôn ngữ phổ biến để hiện thực server. Cấu trúc của những dự án như vậy rất khác nhau, do việc phát triển server có nhiều khía cạnh: giao thức (REST? gRPC?), triển khai, các tệp front-end, đóng gói container, script và nhiều thứ khác. Ở đây chúng tôi sẽ tập trung hướng dẫn vào những phần của dự án được viết bằng Go.

Các dự án server thường sẽ không có package để xuất ra ngoài, bởi một server thường là một binary khép kín (hoặc một nhóm binary). Vì vậy, chúng tôi khuyến nghị giữ các package Go hiện thực logic của server trong thư mục internal. Hơn nữa, vì dự án nhiều khả năng có nhiều thư mục khác chứa tệp không phải Go, giữ tất cả các lệnh Go cùng nhau trong thư mục cmd là một ý hay:

project-root-directory/
  go.mod
  internal/
    auth/
      ...
    metrics/
      ...
    model/
      ...
  cmd/
    api-server/
      main.go
    metrics-analyzer/
      main.go
    ...
  ... the project's other directories with non-Go code

Trong trường hợp repository của server phát sinh những package trở nên hữu ích để chia sẻ với các dự án khác, tốt nhất là tách chúng ra thành các module riêng.

Nội dung gốc: Go Documentation © The Go Authors, giấy phép CC BY 3.0. Bản dịch và ghi chú: Lê Văn Dũng.