π¬ 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
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...
Interface dan Interface Kosong di Golang
Halo teman-teman dev πKita lanjut lagi ke seri Golang Basic, dan kali ini kita akan bahas salah satu konsep penting di Go: interface, termasuk interface kosong (interface{}). Interface adalah pondasi dari polymorphism di Go. Kalau kamu ngerti konsep...
Package, Import, dan Access Modifier di Golang
Halo teman-teman developer πHari ini kita akan bahas salah satu fondasi penting dalam bahasa Go: package, import, dan access modifier. Ini adalah konsep dasar yang wajib banget kamu kuasai sebelum masuk ke hal-hal yang lebih kompleks seperti concurr...