โครงสร้างโปรเจกต์ Go
โปรเจกต์ Go มีโครงสร้างอย่างไร
หัวข้อที่มีชื่อว่า “โปรเจกต์ Go มีโครงสร้างอย่างไร”ถ้ามาจากสาย Node.js/TypeScript คุณคงคุ้นกับ package.json, tsconfig.json, โฟลเดอร์ src/ และ node_modules โปรเจกต์ Go วางโครงสร้างต่างออกไป แต่ก็เป็นระเบียบไม่แพ้กัน เข้าใจโครงสร้างนี้ตั้งแต่ต้นช่วยประหยัดเวลาไปได้หลายชั่วโมง
go.mod — package.json ของ Go
หัวข้อที่มีชื่อว่า “go.mod — package.json ของ Go”go mod init เป็นคนสร้าง go.mod ขึ้นมาเป็น root manifest ของ Go module ในไฟล์นี้จะประกาศชื่อ module (prefix ของ import path ของคุณ) และ Go version ขั้นต่ำที่ต้องใช้
// package.json{ "name": "my-app", "version": "1.0.0", "scripts": { "build": "tsc", "start": "node dist/index.js" }, "dependencies": { "express": "^4.18.2" }, "devDependencies": { "typescript": "^5.0.0" }}// go.modmodule github.com/yourname/myapp
go 1.27
require ( github.com/gin-gonic/gin v1.9.1)
// go.sum is the lockfile (like package-lock.json)// it is auto-managed — never edit it manuallyชื่อ module (github.com/yourname/myapp) ไม่ใช่แค่ชื่อ — แต่คือ import path prefix ที่ใช้ทั่วทั้งโค้ดของคุณ โดย convention มักตั้งให้ตรงกับ URL ของ source repository
Package — หนึ่งต่อ directory
หัวข้อที่มีชื่อว่า “Package — หนึ่งต่อ directory”ใน Go package คือ directory ที่รวมไฟล์ .go ซึ่งประกาศ package name เดียวกันทั้งหมด เข้มงวดกว่า TypeScript/ESM ที่ทุกไฟล์เป็น module ของตัวเอง เพราะใน Go คุณ import ที่ระดับ package ไม่ใช่ระดับไฟล์
// TypeScript: file-level importsimport { UserService } from './services/user.service';import type { User } from './models/user.model';import * as utils from '../shared/utils';// Go: package-level importsimport ( "github.com/yourname/myapp/internal/services" "github.com/yourname/myapp/internal/models" "github.com/yourname/myapp/pkg/utils")
// then use: services.NewUserService(...)// models.User{...}// utils.SomeHelper(...)โครงสร้างโปรเจกต์แบบ Idiomatic
หัวข้อที่มีชื่อว่า “โครงสร้างโปรเจกต์แบบ Idiomatic”ชุมชน Go ลงตัวกับ layout มาตรฐานสำหรับ service และ CLI มาแล้ว ลองมาเทียบกับโปรเจกต์ Node/TypeScript ทั่วไปดู:
// TypeScript / Node.js projectmy-app/ src/ controllers/ # route handlers services/ # business logic models/ # TypeScript interfaces/types middleware/ # Express middleware index.ts # entry point dist/ # compiled output package.json tsconfig.json .env// Go project (idiomatic layout)myapp/ cmd/ myapp/ main.go # entry point (package main) internal/ handlers/ # HTTP handlers services/ # business logic models/ # struct definitions middleware/ # HTTP middleware pkg/ # reusable packages (public API) go.mod go.sum .envกฎสำคัญ:
cmd/เก็บ entry point (mainpackage) — หนึ่ง subdirectory ต่อหนึ่ง binaryinternal/คือโค้ด private ที่ Go บังคับให้ — package ในนี้ module ภายนอก import ไม่ได้pkg/เก็บ package ที่คุณตั้งใจเปิดให้ภายนอกเรียกใช้
Identifier ที่ exported vs unexported
หัวข้อที่มีชื่อว่า “Identifier ที่ exported vs unexported”ใน TypeScript คุณใช้ export กับ import ส่วนใน Go ตัวอักษรแรกของชื่อ identifier เป็นตัวกำหนด visibility — ไม่ต้องมี keyword export
// TypeScript: explicit exportexport interface User { id: string; name: string; }export function greet(name: string): string { return "Hello, " + name + "!";}// internal helper (no export)function formatName(n: string): string { return n.trim(); }// Go: uppercase = exported, lowercase = unexportedtype User struct { // exported (capital U) ID string Name string}
func Greet(name string) string { // exported (capital G) return "Hello, " + formatName(name) + "!"}
func formatName(n string) string { // unexported (lowercase) return strings.TrimSpace(n)}ลองเลย — package และ import
หัวข้อที่มีชื่อว่า “ลองเลย — package และ import”package main
import ( "fmt" "strings")
// Exported function — capital letterfunc Greet(name string) string { return "Hello, " + formatName(name) + "!"}
// unexported helper — lowercase letterfunc formatName(n string) string { return strings.ToUpper(n[:1]) + n[1:]}
func main() { fmt.Println(Greet("gopher")) fmt.Println(Greet("world"))
// The standard library is imported by path // "strings" -> the strings package fmt.Println(strings.ToUpper("go is great"))}Compiling on go.dev (Go 1.27)… offline? an in-browser runtime (~8 MB, Go 1.26) is used instead