โครงสร้างโปรเจกต์ 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.22
require ( github.com/gin-gonic/gin v1.9.1)
// go.sum คือ lockfile (เหมือน package-lock.json)// จัดการอัตโนมัติ — ไม่ควรแก้ด้วยตัวเองชื่อ 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: imports ระดับไฟล์import { UserService } from './services/user.service';import type { User } from './models/user.model';import * as utils from '../shared/utils';// Go: imports ระดับ packageimport ( "github.com/yourname/myapp/internal/services" "github.com/yourname/myapp/internal/models" "github.com/yourname/myapp/pkg/utils")
// แล้วใช้: services.NewUserService(...)// models.User{...}// utils.SomeHelper(...)โครงสร้างโปรเจกต์แบบ Idiomatic
หัวข้อที่มีชื่อว่า “โครงสร้างโปรเจกต์แบบ Idiomatic”ชุมชน Go ลงตัวกับ layout มาตรฐานสำหรับ service และ CLI มาแล้ว ลองมาเทียบกับโปรเจกต์ Node/TypeScript ทั่วไปดู:
// TypeScript / Node.js projectmy-app/ src/ controllers/ # route handler services/ # business logic models/ # TypeScript interface/type 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 handler services/ # business logic models/ # struct definition middleware/ # HTTP middleware pkg/ # package ที่นำกลับมาใช้ได้ (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: export แบบ explicitexport interface User { id: string; name: string; }export function greet(name: string): string { return `Hello, ${name}!`;}// helper ภายใน (ไม่มี export)function formatName(n: string): string { return n.trim(); }// Go: ตัวพิมพ์ใหญ่ = exported, ตัวพิมพ์เล็ก = unexportedtype User struct { // exported (U ตัวใหญ่) ID string Name string}
func Greet(name string) string { // exported (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 — ตัวพิมพ์ใหญ่func Greet(name string) string { return "Hello, " + formatName(name) + "!"}
// unexported helper — ตัวพิมพ์เล็กfunc formatName(n string) string { return strings.ToUpper(n[:1]) + n[1:]}
func main() { fmt.Println(Greet("gopher")) fmt.Println(Greet("world"))
// Standard library import โดย path // "strings" -> the strings package fmt.Println(strings.ToUpper("go is great"))}Loading Go runtime (first run only, ~8 MB)…