โ† Semua pembelajaran / Go Nol โ†’ Enterprise
Fase 0 ยท Fondasi Go & Tooling

Struktur proyek & paket

Go tidak punya struktur folder resmi selain dua konvensi yang benar-benar berarti: cmd/ untuk titik masuk dan internal/ untuk kode yang dijaga compiler agar tidak bisa diimpor dari luar.

Sumber asli go.dev Resmi Rangkuman ~7 menit baca

Intisari

  • internal/ bukan sekadar kesepakatan: compiler menolak import dari luar modul. Ini satu-satunya batas modul yang benar-benar ditegakkan.
  • Satu folder = satu paket. Nama paket mengikuti nama folder, dan sebaiknya satu kata, huruf kecil.
  • cmd/<nama>/main.go untuk tiap binari yang dihasilkan proyek โ€” server, worker, alat migrasi.
  • Impor melingkar adalah error kompilasi. Itu memaksa arah ketergantungan dipikirkan sejak awal.
  • Repo golang-standards/project-layout bukan standar resmi dan kelewat rumit untuk sebagian besar proyek.

Struktur yang dipakai roadmap ini

toko/
โ”œโ”€โ”€ go.mod
โ”œโ”€โ”€ cmd/
โ”‚   โ”œโ”€โ”€ server/main.go        # binari HTTP
โ”‚   โ”œโ”€โ”€ worker/main.go        # binari pemroses antrean
โ”‚   โ””โ”€โ”€ migrate/main.go       # alat migrasi database
โ”œโ”€โ”€ internal/                 # โ† tidak bisa diimpor modul lain. Dipaksa compiler.
โ”‚   โ”œโ”€โ”€ http/                 # handler, middleware, routing
โ”‚   โ”‚   โ”œโ”€โ”€ handler_produk.go
โ”‚   โ”‚   โ””โ”€โ”€ middleware.go
โ”‚   โ”œโ”€โ”€ produk/               # satu domain: model + aturan + penyimpanan
โ”‚   โ”‚   โ”œโ”€โ”€ produk.go
โ”‚   โ”‚   โ”œโ”€โ”€ layanan.go
โ”‚   โ”‚   โ””โ”€โ”€ postgres.go
โ”‚   โ”œโ”€โ”€ pesanan/
โ”‚   โ”œโ”€โ”€ auth/
โ”‚   โ”œโ”€โ”€ config/
โ”‚   โ””โ”€โ”€ platform/             # pembungkus tipis: db, redis, logger, otel
โ”œโ”€โ”€ migrations/               # berkas .sql, berurutan
โ”œโ”€โ”€ web/                      # template, aset statis
โ””โ”€โ”€ deploy/                   # Dockerfile, task definition, workflow

Yang bukan standar: repo populer golang-standards/project-layout sering dikira resmi karena namanya. Ia bukan โ€” tidak berasal dari tim Go, dan strukturnya (pkg/, api/, build/, third_party/โ€ฆ) kelewat berat untuk hampir semua proyek. Panduan resminya adalah halaman sumber materi ini, dan isinya jauh lebih sederhana: cmd/, internal/, selesai.

internal/: satu-satunya batas yang ditegakkan

Aturannya dari compiler, bukan dari kesepakatan tim: paket di dalam internal/ hanya bisa diimpor oleh kode yang berbagi folder induk dengan internal/ itu.

PaketBisa diimpor oleh
contoh.com/toko/internal/produkapa pun di dalam contoh.com/toko/
contoh.com/toko/internal/produkbukan contoh.com/lain โ€” gagal kompilasi
contoh.com/toko/produk (tanpa internal)siapa pun di internet, selamanya

Konsekuensi yang sering tidak disadari: apa pun yang tidak kamu taruh di internal/ otomatis jadi API publik modulmu. Orang bisa mengimpornya, dan mengubahnya nanti jadi breaking change. Aturan praktis untuk aplikasi (bukan pustaka): taruh semuanya di internal/ sampai ada alasan konkret untuk mengeluarkannya.

Paket: satu folder, satu paket

// internal/produk/produk.go
package produk

type Produk struct { ... }

// Dipanggil dari luar sebagai produk.Cari(...)
func Cari(...) { ... }
Nama paketNilai
produk, auth, billingโœ… satu kata, huruf kecil, tanpa garis bawah
util, common, helper, baseโŒ tidak memberi tahu apa pun; jadi tempat sampah
produkService, produk_serviceโŒ camelCase dan garis bawah tidak dipakai untuk paket
produk.ProdukServiceโŒ nama paket sudah jadi awalan โ€” cukup produk.Service

Nama paket ikut terbaca di tempat pemakaian, jadi rancanglah untuk dibaca dari sana: http.Client, bytes.Buffer, produk.Layanan. Pengulangan seperti produk.ProdukLayanan adalah gejala paling umum bahwa penamaannya belum dipikir.

Impor melingkar tidak diizinkan

internal/pesanan  โ†’  internal/produk     โœ…
internal/produk   โ†’  internal/pesanan    โŒ import cycle: gagal kompilasi

Ini terasa mengganggu di minggu pertama dan menyelamatkanmu di tahun kedua. Kalau dua paket saling membutuhkan, biasanya salah satu dari tiga hal ini yang benar:

  1. Keduanya sebenarnya satu paket โ€” gabungkan.
  2. Ada konsep ketiga yang belum punya nama โ€” keluarkan ke paket sendiri.
  3. Salah satu arah seharusnya lewat interface yang dideklarasikan di sisi pemakai (Fase 1 dan 5).

Berkas khusus yang dikenali toolchain

Pola namaArti
*_test.goHanya ikut saat go test, tidak pernah masuk binari
*_linux.go, *_windows.goHanya dikompilasi di OS tersebut
doc.goKonvensi tempat komentar dokumentasi paket
//go:embedMenyisipkan berkas ke dalam binari โ€” template dan migrasi ikut terbawa
import "embed"

//go:embed migrations/*.sql
var migrasiFS embed.FS

//go:embed web/template/*
var templateFS embed.FS

//go:embed adalah pasangan alami dari binari statis. Template, berkas migrasi, dan aset kecil ikut masuk ke dalam satu berkas hasil build. Di Fase 10 hal ini berarti image scratch-mu benar-benar cukup berisi satu berkas โ€” tidak ada folder yang harus disalin dan tidak ada berkas yang bisa hilang di antara build dan deploy.

Kapan memecah paket

SinyalTindakan
Satu berkas > ~800 barisPecah berkas, belum tentu pecah paket
Paket punya dua kelompok tipe yang tidak saling menyentuhPecah jadi dua paket
Terasa perlu util baruBerhenti. Taruh fungsinya di paket yang memakainya
Satu tipe dipakai tiga paket berbedaNaikkan ke paket domain sendiri

Go lebih memilih paket sedikit yang berisi daripada banyak paket berisi satu berkas. Berbeda dari Java atau C#, jumlah berkas per paket tidak dibatasi apa pun โ€” net/http di pustaka standar berisi puluhan berkas dalam satu paket.

Latihan: ubah proyek halo-mu jadi cmd/server/main.go plus internal/halo/halo.go, lalu panggil fungsi dari paket halo. Setelah jalan, buat modul kedua di folder lain dan coba impor contoh.com/halo/internal/halo dari sana โ€” baca pesan errornya. Itu satu-satunya batas arsitektur di Go yang tidak bisa dilanggar siapa pun.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.