Golang Dasar: Dari Nol hingga CLI Production-Ready7/10

Bab 7 dari 10

Package, Struktur Proyek, dan Dependensi

Tujuan Pembelajaran

Setelah menyelesaikan bab ini, Anda mampu:

  • membagi program menjadi package yang kohesif;
  • membuat dan merawat module Go;
  • memilih struktur proyek yang sederhana dan idiomatis;
  • mengelola dependensi dengan go mod;
  • memahami aturan ekspor, import, dan package internal.

1. Package dan Module

Package adalah unit organisasi, kompilasi, dan akses kode. Module adalah kumpulan package berversi yang memiliki berkas go.mod.

Buat proyek baru:

Code
mkdir taskcli
cd taskcli
go mod init example.com/taskcli

Nama module biasanya URL repositori tempat kode akan diterbitkan. Untuk eksperimen lokal, nama seperti example.com/taskcli sah selama import di proyek konsisten.

Setiap direktori normal berisi satu package. Nama package singkat, huruf kecil, dan tidak perlu mengulang konteks: gunakan task, bukan taskutils atau task_package.

2. Ekspor dan Import

Identifier berawalan huruf besar diekspor dari package; identifier berawalan huruf kecil hanya dapat digunakan di package yang sama.

Code
// task/task.go
package task

import (
	"errors"
	"strings"
)

type Task struct {
	ID    int
	Title string
}

func New(id int, title string) (Task, error) {
	title = strings.TrimSpace(title)
	if id < 1 {
		return Task{}, errors.New("ID harus positif")
	}
	if title == "" {
		return Task{}, errors.New("judul tidak boleh kosong")
	}
	return Task{ID: id, Title: title}, nil
}
Code
// cmd/taskcli/main.go
package main

import (
	"fmt"
	"log"

	"example.com/taskcli/task"
)

func main() {
	t, err := task.New(1, "Belajar package")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(t.Title)
}

Import dikelompokkan oleh gofmt: pustaka standar, baris kosong, lalu package module atau pihak ketiga. Hindari dot import karena asal identifier menjadi tidak jelas.

3. Struktur Proyek yang Proporsional

Untuk aplikasi kecil, satu main.go dapat cukup. Pecah direktori setelah ada tanggung jawab nyata yang berbeda.

Code
taskcli/
├── go.mod
├── cmd/
│   └── taskcli/
│       └── main.go
├── internal/
│   └── task/
│       ├── task.go
│       └── store.go
└── README.md

Direktori cmd/taskcli berisi entry point. internal/task menyimpan kode aplikasi yang tidak boleh diimpor module lain. Go menegakkan batas internal saat kompilasi.

Memuat diagram…

Jangan menyalin struktur proyek besar tanpa kebutuhan. Folder generik seperti utils, common, atau helpers sering menyembunyikan tanggung jawab yang tidak jelas.

4. Siklus Import

Go melarang import cycle. Jika package task mengimpor storage, maka storage tidak boleh kembali mengimpor task.

Solusi yang lazim:

  • pindahkan tipe domain ke package yang paling memilikinya;
  • definisikan interface kecil di package pemakai;
  • gabungkan package jika pemisahannya tidak memberi manfaat;
  • pindahkan kode bersama ke package ketiga hanya jika konsepnya benar-benar mandiri.

5. Mengelola Dependensi

go.mod mencatat path module, versi Go, dan kebutuhan module lain. go.sum mencatat checksum untuk verifikasi unduhan.

Code
go mod tidy
go list -m all
go mod graph
go mod verify

go mod tidy menambah dependensi yang diperlukan dan menghapus yang tidak dipakai. Jalankan setelah perubahan import.

Jika memang memerlukan package eksternal:

Code
go get example.com/some/module@v1.2.3
go mod tidy

Gunakan versi eksplisit bila proyek menuntut reproduksibilitas. Evaluasi lisensi, pemeliharaan, keamanan, dan ukuran transitive dependency sebelum menambah library. Pustaka standar sering sudah cukup.

Untuk memperbarui dependensi langsung dan tidak langsung sesuai batas kompatibilitas:

Code
go get -u ./...
go mod tidy
go test ./...

Tinjau perubahan go.mod dan go.sum; jangan memperbarui tanpa menjalankan pengujian.

6. Package Documentation dan API

Dokumentasi identifier yang diekspor dimulai dengan namanya.

Code
// Package task menyediakan model dan operasi tugas.
package task

// Task merepresentasikan satu pekerjaan yang dapat diselesaikan.
type Task struct {
	ID int
}

API package yang baik memiliki permukaan kecil, nama jelas, dan aturan error yang konsisten. Hindari mengekspos representasi internal jika pengguna hanya memerlukan operasi tingkat tinggi.

7. Contoh Proyek Minimal yang Dapat Dikompilasi

Code
hello/
├── go.mod
├── main.go
└── greeting/
    └── greeting.go
Code
// greeting/greeting.go
package greeting

import "strings"

func Hello(name string) string {
	name = strings.TrimSpace(name)
	if name == "" {
		name = "dunia"
	}
	return "Halo, " + name + "!"
}
Code
// main.go
package main

import (
	"fmt"

	"example.com/hello/greeting"
)

func main() {
	fmt.Println(greeting.Hello("Gopher"))
}
Code
go mod init example.com/hello
gofmt -w .
go run .
go test ./...

Kesalahan Umum

  1. Terlalu dini membuat banyak package. Mulai sederhana dan pisahkan saat tanggung jawab jelas.
  2. Nama package mengulang nama tipe. task.Task lebih baik daripada taskmodel.TaskModel.
  3. Mengedit go.sum secara manual. Biarkan tool Go mengelolanya.
  4. Mengabaikan go mod tidy. Module dapat menyimpan kebutuhan usang atau kehilangan dependensi.
  5. Memakai replace lokal lalu lupa menghapusnya. Build pengguna lain akan gagal.
  6. Menciptakan import cycle. Evaluasi kembali arah dependensi atau batas package.
  7. Menambah library untuk fungsi kecil. Periksa pustaka standar terlebih dahulu.

Latihan

  1. Buat module example.com/library dengan package book dan program di cmd/catalog.
  2. Pindahkan package book ke internal/book, lalu coba impor dari module lain dan amati error.
  3. Jalankan go list ./..., go mod tidy, dan go mod verify; jelaskan fungsi tiap perintah.
  4. Temukan package bernama utils pada proyek latihan dan ubah menjadi package dengan tanggung jawab spesifik.
  5. Tambahkan satu dependency kecil hanya untuk eksperimen, periksa go mod graph, lalu hapus import dan jalankan go mod tidy.

Ringkasan

  • Package mengelompokkan kode berdasarkan tanggung jawab; module memberi identitas dan versi pada kumpulan package.
  • Struktur proyek harus tumbuh mengikuti kebutuhan, bukan template yang berlebihan.
  • internal menyediakan batas akses yang ditegakkan toolchain.
  • go mod tidy, verify, dan pengujian menjaga dependensi tetap konsisten dan dapat direproduksi.