Proyek ini terinspirasi dari chanel youtube Review Kodingan Go Yang Sangat Tidak Clean! #Codejam Imre Nagi
Ucapan terima kasih yang sebesar-besarnya kepada Mas Didit Koding aja dulu KJM atas ilmu yang telah dibagikan🙏🏽
Source asli dapat dilihat pada Module Driven Go Todolist
Konfigurasi database dapat dilihat pada repositori todogo-migration
Berbeda dengan yang disebut sebagai 'standard go structure', proyek ini bertujuan untuk menunjukkan bahwa untuk membuat perangkat lunak yang mengikuti arsitektur bawang (onion architecture) di Go, Anda tidak perlu membuat banyak objek dan struktur.
Ketika saya melihat proyek-proyek di luar sana, atau ketika saya membaca proyek dari klien saya, ada pola yang saya kenali. Kebanyakan dari mereka mencoba menyesuaikan Go dengan model mental framework yang mereka kenal.
Jadi, saya lelah melihat kode yang berbelit-belit yang mencoba MEMAKSAKAN orientasi objek ke Go. Inilah mengapa proyek ini menggunakan mostly functions.
Proyek ini bukan contoh sempurna dari perangkat lunak tingkat produksi. Saya membuat objek ini dengan prinsip-prinsip berikut:
- Mudah dipahami dan diajarkan.
- Idiomatik. Ini berarti saya menggunakan Go seperti yang dimaksudkan. Gunakan nilai sebanyak mungkin dan gunakan pola seperti 'accept interface returns value' dan hindari hal-hal seperti mengembalikan antarmuka.
- High cohesion, lose coupling. Hal-hal yang seharusnya bersama harus berada di tempat yang sama.
- Dependensi yang masuk akal. Saya memiliki alasan mengapa saya menggunakan dependensi tersebut.
- Default yang masuk akal. Setiap konfigurasi memiliki default yang memudahkan pengembangan.
Proyek ini sesederhana vanilla. Saya tidak menggunakan ORM apa pun dan berusaha memiliki dependensi yang sangat minimal. Bahkan, proyek ini tidak memiliki antarmuka yang didefinisikan. Jika Anda melihat file go.mod
, Anda akan melihat bahwa ini adalah dependensi yang saya gunakan:
require (
github.com/Masterminds/squirrel v1.5.4
github.com/go-chi/chi/v5 v5.2.0
github.com/jackc/pgx/v5 v5.7.2
github.com/oklog/ulid/v2 v2.1.0
github.com/rs/zerolog v1.33.0
gopkg.in/guregu/null.v4 v4.0.0
gopkg.in/yaml.v3 v3.0.1
)
Saya dapat menjelaskan tujuan dari dependensi di atas, dan mengapa saya memilih dependensi tersebut.
-
go-chi
adalah library routing, membantu mengurai parameter permintaan dan merutekan permintaan. Alasan saya menggunakan library ini adalah karena tidak memiliki dependensi tidak langsung. Chi adalah library routing sederhana dan melakukan pekerjaannya dengan baik. -
pgx
adalah klien PostgreSQL. Saya tidak menggunakandatabase/sql
standar karena biasanya saya menginginkan fitur PostgreSQL yang tidak tersedia pada driver database dengan denominator terendah. -
ulid
adalah library untuk menghasilkan dan membangun ULID. Saya membutuhkan ini karena saya ingin kunci utama domain diurutkan secara leksikografis tetapi juga stateless dan unik. -
null
adalah library yang biasanya saya gunakan untuk menghindari pengecualian pointernil
dan menghindari penggunaan pointer ketika saya dapat menggunakan nilai sebagai gantinya. Dependensi ini bersifat opsional. -
yaml
adalah library untuk mengurai file YAML. Ini juga merupakan dependensi opsional. Jika Anda tidak menyukai YAML, Anda dapat mengubahnya menjadi apa pun yang Anda inginkan. -
squirrel
adalah sebuah library untuk mengenerate kata-kata dalam bahasa SQL yang sesungguhnya😁.
Di root proyek ini ada paket main
yang memiliki main.go
. Ini adalah tempat di mana Anda meletakkan program utama Anda serta konfigurasi runtime.
Di dalam root proyek ada modul yang diimplementasikan sebagai paket Go. Paket adalah batasan dalam Go. Semua yang ada di dalam modul ini diisolasi. Di dalam modul ini no rules tentang bagaimana Anda mengatur file. Namun, dalam proyek saya, saya biasanya memiliki ini:
-
Objek domain. Ini mendefinisikan objek yang mempertahankan statusnya dan dipertahankan. Dalam contoh ini ada di file
todo_item.go
. Di dalam proyek adatodo_item_json.go
ini hanya untuk serialisasi. Saya lebih suka memisahkan representasi JSON di file yang berbeda. -
Repositori. Ini adalah abstraksi di mana Anda dapat mengambil dan menyimpan objek domain Anda. Saya hanya menamainya 'repository' karena agak mirip dengan pola repositori tetapi saya mengimplementasikannya dengan fungsi murni di
repo.go
. -
Penyimpanan. Ini hanyalah tempat di mana Anda terhubung, membaca, dan menulis di repositori Anda. Dalam contoh ini, sangat sederhana, hanya objek
pool
global yang mewakili kumpulan koneksi ke instance PostgreSQL. Itu ada didb.go
. -
Model baca. Ini adalah tipe dan struktur untuk mewakili sebagian atau agregasi data dari penyimpanan. Anda cannot modify model baca karena seperti namanya, ini untuk membaca.
-
Layanan. Ini adalah file dengan fungsi yang mendefinisikan batas transaksi. Layanan ini agnostik dengan protokol. Tidak boleh ada data terkait protokol di sini seperti Kode Respons HTTP. Juga diimplementasikan dalam fungsi murni di
service.go
. -
Protokol. Ini adalah tempat di mana Anda meletakkan penangan (handlers) untuk permintaan Anda. Saya tidak meletakkannya di paket terpisah
handlers
karena saya tidak ingin menggunakan sesuatu sepertiCreateTodoItemHandler
dan sebagai gantinya saya hanya dapat menggunakannya langsung pada parameter function dari router tersebut sepertiroute.Get("/", func() {...})
. Di dalam modul adaprotokol.go
. Setiap rute untuk modul ini ada di sini. Ini akan mengekspor objek*chi.Mux
yang mengimplementasikan antarmukachi.Router
. Ini mengisolasi perubahan apa pun ke subrouter ke modul ini. Dimain.go
Anda hanya dapat memasang router.
Catatan Sebelumnya, sebagian besar fungsi diekspor. Pada versi ini, saya telah membuat fungsi seprivat mungkin.
Fungsi yang diekspor di setiap area modul adalah sebagai berikut:
SetPool()
untuk menyiapkan kumpulan koneksi database global.Router()
untuk mengekspor objekchi.Mux
untuk dipasang.- Objek domain, ini opsional. Jika Anda ingin menyembunyikan dan mengisolasi objek domain Anda, maka Anda dapat membuatnya privat.
Saya jarang menggunakan mock. Baca alasannya di sini. Saya menggunakan fake. Hal baik tentang Go adalah memungkinkan tag build yang akan memungkinkan kompilasi bersyarat file. Karena saya menggunakan file untuk menandai batas dan tanggung jawab, ini bekerja dengan baik.
Lihatlah repo.go
dan repo_fake.go
untuk perbandingan.
Untuk membangun atau menjalankan program dengan implementasi palsu, Anda dapat menggunakan parameter -tags
, dan versi palsu akan dikompilasi sebagai gantinya.
# Build or run
go build -tags=fake
Versi awal program ini tidak memiliki file konfigurasi dan semua parameter di-hardcode. Pada awalnya saya pikir itu sudah cukup. Namun, saya pikir memberikan contoh tentang bagaimana mengimplementasikan server yang mematuhi aturan 12 Factor App adalah penting.
Lihat config.go
. File ini berisi kode untuk mengurai file konfigurasi dari dua sumber: variabel lingkungan dan file konfigurasi. File konfigurasi memiliki prioritas. Ini adalah variabel lingkungan, jalur kunci file konfigurasi, dan nilai default.
Variabel Lingkungan | Jalur kunci YAML | Nilai default | Deskripsi |
---|---|---|---|
KAD_LISTEN_HOST |
listen.host |
"127.0.0.1" | Alamat Server |
KAD_LISTEN_PORT |
listen.port |
8080 | Port Server |
KAD_DB_HOST |
db.host |
"127.0.0.1" | Host Postgres |
KAD_DB_PORT |
db.port |
5432 | Port Postgres |
KAD_DB_NAME |
db.dbname |
"todo" | Nama Database |
KAD_DB_USERNAME |
db.username |
"John" | Username Database |
KAD_DB_PASSWORD |
db.password |
"example" | Password Database |
KAD_DB_SSL |
db.ssl_mode |
"disable" | Mode SSL |
Nilai default, jika kita mengungkapkannya dalam file konfigurasi adalah sebagai berikut.
listen:
host: 127.0.0.1
port: 8080
db:
dbname: todo
host: 127.0.0.1
port: 5432
ssl_mode: disable
username: John
password: example
Program akan mencari config.yaml
di direktori kerja saat ini, atau Anda dapat memberikan flag -c
untuk memaksa program menggunakan nama file konfigurasi Anda sendiri. Misalnya, Anda dapat menjalankannya dengan sesuatu seperti ini:
./app -c someconfig.yml
Proyek ini adalah heuristik, bukan panduan atau 'framework' struktur. Ini untuk menunjukkan bahwa Anda dapat memiliki struktur kode dan arsitektur yang masuk akal dengan tetap berpegang pada kesederhanaan Go.
go build -ldflags "-s -w" -o myApp
# atau pada windows
GOOS=windows GOARCH=amd64 go build -o app.exe -ldflags="-s -w"
Copyright (c) 2025 Baroen Sudarman
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.