Struktur Proyek Go: Praktik & Pola
Susun proyek Go Anda untuk skalabilitas dan kejelasan
Menyusun Proyek Go secara efektif adalah fondasi penting untuk pemeliharaan jangka panjang, kolaborasi tim, dan skalabilitas. Berbeda dengan kerangka kerja yang memaksa tata letak direktori kaku, Go mendorong fleksibilitas—tapi dengan kebebasan itu datang tanggung jawab untuk memilih pola yang sesuai dengan kebutuhan spesifik proyek Anda.

Memahami Filosofi Go terhadap Struktur Proyek
Filosofi desain minimalis Go diterapkan pada organisasi proyek. Bahasa ini tidak menentukan struktur spesifik, tetapi mempercayakan pengembang untuk membuat keputusan yang terinformasi. Pendekatan ini telah membawa komunitas untuk mengembangkan beberapa pola terbukti, mulai dari tata letak datar sederhana untuk proyek kecil hingga arsitektur canggih untuk sistem enterprise.
Prinsip utama adalah sederhana terlebih dahulu, kompleks ketika diperlukan. Banyak pengembang terjebak dalam perangkap mengoverdesain struktur awal mereka, menciptakan direktori yang sangat bersarang dan abstraksi yang terlalu dini. Mulailah dengan apa yang Anda butuhkan hari ini, dan refactor saat proyek Anda berkembang.
Kapan Saya Harus Menggunakan Direktori internal/ versus pkg/?
Direktori internal/ memiliki tujuan spesifik dalam desain Go: berisi paket yang tidak dapat diimpor oleh proyek eksternal. Compiler Go memaksa pembatasan ini, menjadikan internal/ sempurna untuk logika aplikasi pribadi, aturan bisnis, dan utilitas yang tidak ditujukan untuk digunakan di luar proyek Anda.
Di sisi lain, direktori pkg/ menunjukkan bahwa kode dimaksudkan untuk dikonsumsi eksternal. Hanya tempatkan kode di sini jika Anda sedang membangun library atau komponen yang dapat digunakan orang lain. Banyak proyek sama sekali tidak memerlukan pkg/—jika kode Anda tidak dikonsumsi secara eksternal, menjaga kode tersebut di akar atau dalam internal/ lebih bersih. Saat membangun library yang dapat digunakan kembali, pertimbangkan untuk memanfaatkan Go generics untuk membuat komponen yang aman tipe dan dapat digunakan kembali.
Tata Letak Standar Proyek Go
Polanya yang paling dikenal luas adalah golang-standards/project-layout, meskipun penting untuk dicatat bahwa ini bukan standar resmi. Berikut adalah tampilan umum dari struktur tersebut:
myproject/
├── cmd/
│ ├── api/
│ │ └── main.go
│ └── worker/
│ └── main.go
├── internal/
│ ├── auth/
│ ├── storage/
│ └── transport/
├── pkg/
│ ├── logger/
│ └── crypto/
├── api/
│ └── openapi.yaml
├── config/
│ └── config.yaml
├── scripts/
│ └── deploy.sh
├── go.mod
├── go.sum
└── README.md
Direktori cmd/
Direktori cmd/ berisi titik masuk aplikasi Anda. Setiap subdirektori mewakili biner eksekutable terpisah. Sebagai contoh, cmd/api/main.go membangun server API Anda, sementara cmd/worker/main.go mungkin membangun prosesor pekerjaan latar belakang.
Praktik terbaik: Pertahankan file main.go Anda sekecil mungkin—cukup untuk menghubungkan dependensi, memuat konfigurasi, dan memulai aplikasi. Semua logika substantif harus berada dalam paket yang diimpor oleh main.go.
// cmd/api/main.go
package main
import (
"log"
"myproject/internal/server"
"myproject/internal/config"
)
func main() {
cfg, err := config.Load()
if err != nil {
log.Fatal(err)
}
srv := server.New(cfg)
if err := srv.Start(); err != nil {
log.Fatal(err)
}
}
Direktori internal/
Ini adalah tempat kode aplikasi pribadi Anda berada. Compiler Go mencegah proyek eksternal mengimpor paket dalam internal/, menjadikannya ideal untuk:
- Logika bisnis dan model domain
- Layanan aplikasi
- API internal dan antarmuka
- Repositori database (untuk memilih ORM yang tepat, lihat perbandingan ORMs Go untuk PostgreSQL)
- Logika otentikasi dan otorisasi
Susun internal/ berdasarkan fitur atau domain, bukan berdasarkan lapisan teknis. Sebagai ganti dari internal/handlers/, internal/services/, internal/repositories/, preferensi internal/user/, internal/order/, internal/payment/ di mana setiap paket berisi handler, service, dan repository-nya.
Apakah Saya Harus Memulai dengan Struktur Direktori yang Kompleks?
Tidak sama sekali. Jika Anda sedang membangun alat kecil atau prototipe, mulailah dengan:
myproject/
├── main.go
├── go.mod
└── go.sum
Saat proyek Anda berkembang dan Anda mengidentifikasi kelompok logis, tambahkan direktori. Anda mungkin menambahkan paket db/ ketika logika database menjadi signifikan, atau paket api/ ketika handler HTTP semakin banyak. Biarkan struktur muncul secara alami, bukan dipaksakan sejak awal.
Struktur Datar vs. Bersarang: Menemukan Keseimbangan
Salah satu kesalahan paling umum dalam struktur proyek Go adalah bersarang terlalu dalam. Go memfavoritkan hierarki yang dangkal—biasanya satu atau dua tingkat dalam. Bersarang yang dalam meningkatkan beban kognitif dan membuat impor menjadi rumit.
Apa Kesalahan Terbesar yang Umum Terjadi?
Bersarang terlalu dalam direktori: Hindari struktur seperti internal/services/user/handlers/http/v1/. Ini menciptakan kompleksitas navigasi yang tidak perlu. Sebaliknya, gunakan internal/user/handler.go.
Nama paket yang umum: Nama seperti utils, helpers, common, atau base adalah aroma kode yang tidak baik. Mereka tidak menyampaikan fungsi spesifik dan sering menjadi tempat pembuangan untuk kode yang tidak terkait. Gunakan nama yang deskriptif: validator, auth, storage, cache.
Ketergantungan sirkular: Ketika paket A mengimpor paket B, dan B mengimpor A, Anda memiliki ketergantungan sirkular—kesalahan kompilasi di Go. Ini biasanya menunjukkan pemisahan tanggung jawab yang buruk. Masukkan antarmuka atau ekstrak tipe yang dibagikan ke dalam paket terpisah.
Campuran tanggung jawab: Pertahankan handler HTTP fokus pada konsern HTTP, repositori database pada akses data, dan logika bisnis dalam paket layanan. Menempatkan aturan bisnis di handler membuat pengujian sulit dan menghubungkan logika domain Anda ke HTTP.
Desain Berbasis Domain dan Arsitektur Hexagonal
Untuk aplikasi yang lebih besar, terutama mikroservis, Desain Berbasis Domain (DDD) dengan Arsitektur Hexagonal memberikan pemisahan tanggung jawab yang jelas.
Bagaimana Saya Menyusun Mikroservis Mengikuti Desain Berbasis Domain?
Arsitektur Hexagonal mengorganisasi kode dalam lapisan konsentris dengan ketergantungan mengalir ke dalam:
internal/
├── domain/
│ └── user/
│ ├── entity.go # Model domain dan objek nilai
│ ├── repository.go # Antarmuka repositori (port)
│ └── service.go # Layanan domain
├── application/
│ └── user/
│ ├── create_user.go # Use case: buat pengguna
│ ├── get_user.go # Use case: ambil pengguna
│ └── service.go # Orkestrasi layanan aplikasi
├── adapter/
│ ├── http/
│ │ └── user_handler.go # Adapter HTTP (endpoint REST)
│ ├── postgres/
│ │ └── user_repo.go # Adapter database (implementasi port repositori)
│ └── redis/
│ └── cache.go # Adapter cache
└── api/
└── http/
└── router.go # Konfigurasi rute dan middleware
Lapisan domain (domain/): Logika bisnis inti, entitas, objek nilai, dan antarmuka layanan domain. Lapisan ini tidak memiliki ketergantungan pada sistem eksternal—tidak ada HTTP, tidak ada impor database. Ia mendefinisikan antarmuka repositori (port) yang diimplementasikan oleh adapter.
Lapisan aplikasi (application/): Use case yang mengorkestrasi objek domain. Setiap use case (misalnya, “buat pengguna”, “proses pembayaran”) adalah file atau paket terpisah. Lapisan ini mengkoordinasikan objek domain tetapi tidak mengandung aturan bisnis sendiri.
Lapisan adapter (adapter/): Mengimplementasikan antarmuka yang didefinisikan oleh lapisan dalam. Handler HTTP mengubah permintaan menjadi objek domain, repositori database mengimplementasikan penyimpanan, antrian pesan menangani komunikasi asinkron. Lapisan ini berisi semua kode spesifik framework dan infrastruktur.
Lapisan API (api/): Rute, middleware, DTO (Data Transfer Object), penamaan versi API, dan spesifikasi OpenAPI.
Struktur ini memastikan logika bisnis inti Anda tetap dapat diuji dan independen dari framework, database, atau layanan eksternal. Anda dapat mengganti PostgreSQL dengan MongoDB, atau REST dengan gRPC, tanpa menyentuh kode domain.
Pola Praktis untuk Jenis Proyek Berbeda
Alat CLI Kecil
Untuk aplikasi CLI, Anda ingin struktur yang mendukung beberapa perintah dan subperintah. Pertimbangkan untuk menggunakan framework seperti Cobra untuk struktur perintah dan Viper untuk manajemen konfigurasi. Panduan kami tentang membangun aplikasi CLI dalam Go dengan Cobra & Viper membahas pola ini secara detail.
mytool/
├── main.go
├── command/
│ ├── root.go
│ └── version.go
├── go.mod
└── README.md
Layanan REST API
Saat membangun REST API dalam Go, struktur ini memisahkan konsern secara bersih: handler menangani konsern HTTP, service berisi logika bisnis, dan repositori mengelola akses data. Untuk panduan komprehensif yang mencakup pendekatan library standar, framework, otentikasi, pola pengujian, dan best practices untuk layanan backend yang siap produksi, lihat panduan kami tentang membangun REST API dalam Go.
myapi/
├── cmd/
│ └── api/
│ └── main.go
├── internal/
│ ├── config/
│ ├── middleware/
│ ├── user/
│ │ ├── handler.go
│ │ ├── service.go
│ │ └── repository.go
│ └── product/
│ ├── handler.go
│ ├── service.go
│ └── repository.go
├── pkg/
│ └── httputil/
├── go.mod
└── README.md
Monorepo dengan Banyak Layanan
myproject/
├── cmd/
│ ├── api/
│ ├── worker/
│ └── scheduler/
├── internal/
│ ├── shared/ # Paket internal yang dibagikan
│ ├── api/ # Kode spesifik API
│ ├── worker/ # Kode spesifik worker
│ └── scheduler/ # Kode spesifik scheduler
├── pkg/ # Library yang dibagikan
├── go.work # File workspace Go
└── README.md
Pengujian dan Dokumentasi
Letakkan file pengujian bersama dengan kode yang diujinya menggunakan sufiks _test.go:
internal/
└── user/
├── service.go
├── service_test.go
├── repository.go
└── repository_test.go
Konvensi ini menjaga pengujian dekat dengan implementasi, membuatnya mudah ditemukan dan dipelihara. Untuk pengujian integrasi yang menyentuh beberapa paket, buat direktori test/ terpisah di akar proyek. Untuk panduan menyeluruh tentang menulis pengujian unit yang efektif, termasuk pengujian tabel, mock, analisis cakupan, dan best practices, lihat panduan kami tentang struktur pengujian unit dalam Go.
Dokumentasi sebaiknya berada di:
README.md: Gambaran proyek, instruksi pengaturan, penggunaan dasardocs/: Dokumentasi rinci, keputusan arsitektur, referensi APIapi/: Spesifikasi OpenAPI/Swagger, definisi protobuf
Untuk API REST, menghasilkan dan menyajikan dokumentasi OpenAPI dengan Swagger sangat penting untuk keterbacaan API dan pengalaman pengembang. Panduan kami tentang menambahkan Swagger ke API Go mencakup integrasi dengan framework populer dan best practices.
Mengelola Ketergantungan dengan Go Modules
Setiap proyek Go seharusnya menggunakan Go Modules untuk manajemen ketergantungan. Untuk referensi menyeluruh tentang perintah Go dan manajemen modul, lihat Go Cheatsheet. Inisialisasi dengan:
go mod init github.com/yourusername/myproject
Ini menciptakan go.mod (ketergantungan dan versi) dan go.sum (checksum untuk verifikasi). Simpan file-file ini dalam kontrol versi untuk pembuatan yang dapat diulang.
Perbarui ketergantungan secara berkala:
go get -u ./... # Perbarui semua ketergantungan
go mod tidy # Hapus ketergantungan yang tidak digunakan
go mod verify # Verifikasi checksum
Kesimpulan Penting
-
Mulai sederhana, berkembang secara alami: Jangan mengoverdesain struktur awal Anda. Tambahkan direktori dan paket ketika kompleksitas memerlukannya.
-
Favoritkan hierarki datar: Batasi bersarang hingga satu atau dua tingkat. Struktur paket datar Go meningkatkan keterbacaan.
-
Gunakan nama paket yang deskriptif: Hindari nama umum seperti
utils. Namai paket berdasarkan fungsinya:auth,storage,validator. -
Pisahkan konsern secara jelas: Pertahankan handler fokus pada HTTP, repositori pada akses data, dan logika bisnis dalam paket layanan.
-
Manfaatkan
internal/untuk privasi: Gunakannya untuk kode yang tidak boleh diimpor secara eksternal. Sebagian besar kode aplikasi berada di sini. -
Terapkan pola arsitektur ketika diperlukan: Untuk sistem kompleks, Arsitektur Hexagonal dan DDD menyediakan batas yang jelas dan kemudahan pengujian.
-
Biarkan Go mengarahkan Anda: Ikuti idiom Go, bukan mengimpor pola dari bahasa lain. Go memiliki filosofi sendiri tentang kesederhanaan dan organisasi.
Tautan yang Berguna
- Go Cheatsheet
- Membangun REST API dalam Go: Panduan Lengkap
- Membangun Aplikasi CLI dalam Go dengan Cobra & Viper
- Pengujian Unit dalam Go: Struktur & Best Practices
- Menambahkan Swagger ke API Go
- Generics dalam Go: Kasus Penggunaan dan Pola
- Perbandingan ORMs Go untuk PostgreSQL: GORM vs Ent vs Bun vs sqlc
Artikel Terkait Lainnya
- Tata Letak Standar Proyek Go - Panduan struktur proyek yang didorong komunitas
- Referensi Go Modules - Dokumentasi resmi Go modules
- Arsitektur Hexagonal dalam Go - Framework arsitektur hexagonal untuk enterprise-grade
- Desain Berbasis Domain dalam Go - Implementasi DDD yang siap produksi
- Konvensi Struktur Proyek Go - Pola dan contoh tambahan
- Effective Go - Panduan best practices resmi Go