π¬ Golang Ticket Booking System from Scratch β No Framework (Part 1)
Pada part pertama ini, kita akan memulai dari nol:
- Membuat struktur folder project
- Setup database MySQL
- Install dependency driver SQL
- Membuat repository awal
- Menulis unit test untuk function
GetSeatStatus, termasuk test error case
π§± Step 1: Inisialisasi Proyek
mkdir ticket-booking
cd ticket-booking
go mod init github.com/fardannozami/ticket-bookingπ’οΈ Step 2: Setup Database (MySQL)
Masuk ke MySQL dan jalankan query berikut:
CREATE TABLE users(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL
);
CREATE TABLE events(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
quota INT NOT NULL
);
CREATE TABLE seats(
id INT PRIMARY KEY AUTO_INCREMENT,
event_id INT NOT NULL,
seat_number VARCHAR(30) NOT NULL,
status ENUM('AVAILABLE', 'BOOKED') DEFAULT 'AVAILABLE',
FOREIGN KEY(event_id) REFERENCES events(id) ON DELETE CASCADE
);
CREATE TABLE bookings(
id INT PRIMARY KEY AUTO_INCREMENT,
event_id INT NOT NULL,
seat_id INT NOT NULL,
user_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_booking(event_id, seat_id, user_id),
FOREIGN KEY(event_id) REFERENCES events(id) ON DELETE CASCADE,
FOREIGN KEY(seat_id) REFERENCES seats(id) ON DELETE CASCADE,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);π¦ Step 3: Install Dependency Driver MySQL
go get github.com/go-sql-driver/mysqlπ Step 4: Struktur Folder dan Booking Repository
Buat folder:
mkdir repositoryFile: repository/booking_repository.go
package repository
import (
"context"
"database/sql"
"github.com/fardannozami/ticket-booking/helper"
)
type BookingRepository interface {
GetSeatStatus(ctx context.Context, tx *sql.Tx, seatId int) string
}
type BookingRepositoryImpl struct{}
func NewBookingRepository() BookingRepository {
return &BookingRepositoryImpl{}
}
func (repo *BookingRepositoryImpl) GetSeatStatus(ctx context.Context, tx *sql.Tx, seatId int) string {
var status string
SQL := `SELECT status FROM seats WHERE id = ?`
err := tx.QueryRowContext(ctx, SQL, seatId).Scan(&status)
helper.PanicIfError(err)
return status
}π°οΈ Step 5: Buat Helper helper/error.go
package helper
func PanicIfError(err error) {
if err != nil {
panic(err)
}
}π°οΈ Step 6: Unit Test untuk GetSeatStatus
sebelumnya install dependency untuk testing dulu
go get github.com/stretchr/testify/assertFile: repository/booking_repository_test.go
package repository
import (
"context"
"database/sql"
"os"
"testing"
"github.com/fardannozami/ticket-booking/helper"
"github.com/stretchr/testify/assert"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
func TestMain(m *testing.M) {
var err error
db, err = sql.Open("mysql", "root@tcp(127.0.0.1:3306)/ticket-booking?charset=utf8mb4&parseTime=True&loc=Local")
helper.PanicIfError(err)
exitCode := m.Run()
db.Close()
os.Exit(exitCode)
}
func insertEvent(id, quota int, name string) {
SQL := `INSERT INTO events (id, quota, name) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE quota = VALUES(quota), name = VALUES(name)`
_, err := db.Exec(SQL, id, quota, name)
helper.PanicIfError(err)
}
func insertSeat(id, eventId int, status, seatNumber string) {
SQL := `INSERT INTO seats (id, event_id, status, seat_number) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE status = VALUES(status), seat_number = VALUES(seat_number)`
_, err := db.Exec(SQL, id, eventId, status, seatNumber)
helper.PanicIfError(err)
}
func TestGetSeatStatus(t *testing.T) {
insertEvent(1, 100, "Event 1")
insertSeat(9, 1, "AVAILABLE", "A1")
repo := NewBookingRepository()
ctx := context.Background()
tx, err := db.BeginTx(ctx, nil)
helper.PanicIfError(err)
defer tx.Rollback()
status := repo.GetSeatStatus(ctx, tx, 9)
assert.Equal(t, "AVAILABLE", status)
}More Articles
You might also like
Claim Management System - Filament v4 Setup & Workflow Awal
Di Part 2, kita sudah: melakukan setup project Laravel 12 mendesain database schema yang realistis dan audit-friendly Sekarang di Part 3, kita mulai membangun UI dan workflow awal menggunakan Fila
Tipe Data Array, Slice, dan Map di Golang
Setelah sebelumnya kita sudah membahas tipe data string, boolean dan number, sekarang kita lanjutkan dengan beberapa tipe data penting yang sering digunakan, yaitu Array, Slice, dan Map. Array digunakan untuk menyimpan sejumlah elemen dengan panjan...
Best Practice dan Kapan Sebaiknya Tidak Menggunakan Goroutine
Halo teman-teman! πKita sudah membahas banyak hal: mulai dari membuat goroutine, channel, race condition, WaitGroup, hingga buffered channel dan select statement. Nah, di seri terakhir ini, kita akan: Merangkum best practice saat menggunakan gorout...