1. Gambaran Mekanisme Migrasi

1.1 Konsep dan Peran Migrasi

Migrasi database adalah proses penyelarasan perubahan dalam model data ke struktur database, yang sangat penting untuk persistensi data. Saat versi aplikasi berkembang, model data sering mengalami perubahan, seperti menambahkan atau menghapus bidang, atau memodifikasi indeks. Migrasi memungkinkan pengembang mengelola perubahan ini dengan cara yang terversion dan sistematis, memastikan konsistensi antara struktur database dan model data.

Dalam pengembangan web modern, mekanisme migrasi menyediakan manfaat berikut:

  1. Kontrol Versi: File migrasi dapat melacak riwayat perubahan struktur database, memudahkan untuk mundur dan memahami perubahan di setiap versi.
  2. Implementasi Otomatis: Melalui mekanisme migrasi, penerapan dan pembaruan database dapat diotomatisasi, mengurangi kemungkinan intervensi manual dan risiko kesalahan.
  3. Kolaborasi Tim: Berkas migrasi memastikan anggota tim menggunakan struktur database yang disinkronkan dalam lingkungan pengembangan yang berbeda, memudahkan pengembangan kolaboratif.

1.2 Fitur Migrasi Framework ent

Integrasi framework ent dengan mekanisme migrasi menawarkan fitur-fitur berikut:

  1. Pemrograman Deklaratif: Pengembang hanya perlu fokus pada representasi Go dari entitas, dan framework ent akan menangani konversi entitas ke tabel database.
  2. Migrasi Otomatis: ent dapat secara otomatis membuat dan memperbarui struktur tabel database tanpa perlu menulis pernyataan DDL secara manual.
  3. Kontrol Fleksibel: ent menyediakan berbagai opsi konfigurasi untuk mendukung berbagai kebutuhan migrasi, seperti dengan atau tanpa batasan kunci asing, dan menghasilkan ID unik global.

2. Pengantar Migrasi Otomatis

2.1 Prinsip Dasar Migrasi Otomatis

Fitur migrasi otomatis framework ent didasarkan pada file definisi skema (biasanya ditemukan dalam direktori ent/schema) untuk menghasilkan struktur database. Setelah pengembang mendefinisikan entitas dan hubungan, ent akan memeriksa struktur yang ada dalam database dan menghasilkan operasi yang sesuai untuk membuat tabel, menambahkan atau memodifikasi kolom, membuat indeks, dll.

Selain itu, prinsip migrasi otomatis dari ent berfungsi dalam "mode tambahan": secara default, hanya menambahkan tabel baru, indeks baru, atau menambahkan kolom ke tabel, dan tidak menghapus tabel atau kolom yang sudah ada. Desain ini bermanfaat untuk mencegah kehilangan data secara tidak sengaja dan memudahkan untuk memperluas struktur database secara maju.

2.2 Menggunakan Migrasi Otomatis

Langkah-langkah dasar untuk menggunakan migrasi otomatis ent adalah sebagai berikut:

package main

import (
    "context"
    "log"
    "ent"
)

func main() {
    client, err := ent.Open("mysql", "root:pass@tcp(localhost:3306)/test")
    if err != nil {
        log.Fatalf("Gagal terhubung ke MySQL: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Lakukan migrasi otomatis untuk membuat atau memperbarui skema database
    if err := client.Schema.Create(ctx); err != nil {
        log.Fatalf("Gagal membuat skema database: %v", err)
    }
}

Pada kode di atas, ent.Open bertanggung jawab untuk menjalin koneksi dengan database dan mengembalikan instance klien, sedangkan client.Schema.Create menjalankan operasi migrasi otomatis aktual.

3. Aplikasi Lanjutan dari Migrasi Otomatis

3.1 Menghapus Kolom dan Indeks

Dalam beberapa kasus, mungkin perlu menghapus kolom atau indeks yang tidak lagi diperlukan dari skema database. Pada saat ini, kita dapat menggunakan opsi WithDropColumn dan WithDropIndex. Contohnya:

// Jalankan migrasi dengan opsi untuk menghapus kolom dan indeks.
err = client.Schema.Create(
    ctx,
    migrate.WithDropIndex(true),
    migrate.WithDropColumn(true),
)

Potongan kode ini memungkinkan konfigurasi untuk menghapus kolom dan indeks selama migrasi otomatis. ent akan menghapus semua kolom dan indeks yang tidak ada dalam definisi skema saat mengeksekusi migrasi.

3.2 ID Global Unik

Secara default, kunci utama dalam database SQL dimulai dari 1 untuk setiap tabel, dan jenis entitas yang berbeda mungkin menggunakan ID yang sama. Pada beberapa skenario aplikasi, seperti saat menggunakan GraphQL, mungkin perlu untuk menyediakan keunikkan global untuk ID objek dari jenis entitas yang berbeda. Pada ent, hal ini dapat dikonfigurasi menggunakan opsi WithGlobalUniqueID:

// Jalankan migrasi dengan ID unik universal untuk setiap entitas.
if err := client.Schema.Create(ctx, migrate.WithGlobalUniqueID(true)); err != nil {
    log.Fatalf("Gagal membuat skema database: %v", err)
}

Setelah mengaktifkan opsi WithGlobalUniqueID, ent akan memberikan rentang ID 2^32 untuk setiap entitas dalam tabel bernama ent_types untuk mencapai keunikan global.

3.3 Mode Offline

Mode offline memungkinkan penulisan perubahan skema ke io.Writer daripada menjalankannya langsung pada database. Ini berguna untuk memverifikasi perintah SQL sebelum perubahan berlaku, atau untuk menghasilkan skrip SQL untuk eksekusi manual. Contohnya:

// Dump perubahan migrasi ke file
f, err := os.Create("migrate.sql")
if err != nil {
    log.Fatalf("Gagal membuat file migrasi: %v", err)
}
defer f.Close()
if err := client.Schema.WriteTo(ctx, f); err != nil {
    log.Fatalf("Gagal mencetak perubahan skema database: %v", err)
}

Kode ini akan menulis perubahan migrasi ke file bernama migrate.sql. Secara praktis, pengembang dapat memilih untuk langsung mencetak ke output standar atau menulis ke file untuk peninjauan atau pencatatan.

4. Mendukung Kunci Asing dan Pengait Kustom

4.1 Mengaktifkan atau Menonaktifkan Kunci Asing

Di Ent, kunci asing diimplementasikan dengan mendefinisikan hubungan (edge) antara entitas, dan hubungan kunci asing ini secara otomatis dibuat pada tingkat database untuk menegakkan integritas dan konsistensi data. Namun, dalam situasi tertentu, seperti untuk optimisasi kinerja atau ketika database tidak mendukung kunci asing, Anda dapat memilih untuk menonaktifkannya.

Untuk mengaktifkan atau menonaktifkan kendala kunci asing dalam migrasi, Anda dapat mengontrolnya melalui opsi konfigurasi WithForeignKeys:

// Mengaktifkan kunci asing
err = client.Schema.Create(
    ctx,
    migrate.WithForeignKeys(true), 
)
if err != nil {
    log.Fatalf("Gagal membuat sumber skema dengan kunci asing: %v", err)
}

// Menonaktifkan kunci asing
err = client.Schema.Create(
    ctx,
    migrate.WithForeignKeys(false), 
)
if err != nil {
    log.Fatalf("Gagal membuat sumber skema tanpa kunci asing: %v", err)
}

Opsi konfigurasi ini perlu dilewatkan saat memanggil Schema.Create, dan ini menentukan apakah untuk menyertakan kendala kunci asing dalam DDL yang dihasilkan berdasarkan nilai yang ditentukan.

4.2 Penerapan Pengait Migrasi

Pengait migrasi adalah logika kustom yang dapat dimasukkan dan dieksekusi pada berbagai tahap eksekusi migrasi. Mereka sangat berguna untuk melakukan logika tertentu pada database sebelum/setelah migrasi, seperti memvalidasi hasil migrasi dan memasukkan data sebelumnya.

Berikut adalah contoh cara mengimplementasikan pengait migrasi kustom:

func customHook(next schema.Creator) schema.Creator {
    return schema.CreateFunc(func(ctx context.Context, tables ...*schema.Table) error {
        // Kode kustom yang akan dieksekusi sebelum migrasi
        // Misalnya, logging, memeriksa prasyarat tertentu, dll.
        log.Println("Logika kustom sebelum migrasi")
        
        // Panggil pengait berikutnya atau logika migrasi default
        err := next.Create(ctx, tables...)
        if err != nil {
            return err
        }
        
        // Kode kustom yang akan dieksekusi setelah migrasi
        // Misalnya, pembersihan, migrasi data, pemeriksaan keamanan, dll.
        log.Println("Logika kustom setelah migrasi")
        return nil
    })
}

// Menggunakan pengait kustom dalam migrasi
err := client.Schema.Create(
    ctx,
    schema.WithHooks(customHook),
)
if err != nil {
    log.Fatalf("Kesalahan menerapkan pengait migrasi kustom: %v", err)
}

Pengait adalah alat yang kuat dan sangat diperlukan untuk migrasi kompleks, memberikan Anda kemampuan untuk secara langsung mengendalikan perilaku migrasi database ketika diperlukan.

5. Migrasi Versi

5.1 Pengenalan ke Migrasi Versi

Migrasi versi adalah pola untuk mengelola migrasi database, memungkinkan pengembang untuk membagi perubahan struktur database ke dalam beberapa versi, masing-masing berisi kumpulan perintah modifikasi database tertentu. Dibandingkan dengan Migrasi Otomatis, migrasi versi memberikan kontrol yang lebih halus, memastikan pelacakan dan pemunduran perubahan struktur database.

Keuntamaan utama migrasi versi adalah dukungannya untuk migrasi maju dan mundur (yaitu, peningkatan atau penurunan), memungkinkan pengembang untuk menerapkan, membatalkan, atau melewati perubahan tertentu sesuai kebutuhan. Saat berkolaborasi dalam sebuah tim, migrasi versi memastikan setiap anggota bekerja pada struktur database yang sama, mengurangi masalah yang disebabkan oleh ketidaksesuaian.

Migrasi otomatis seringkali tidak dapat diputar balik, menghasilkan dan menjalankan pernyataan SQL untuk mencocokkan keadaan terkini dari model entitas, yang terutama digunakan dalam tahap pengembangan atau proyek kecil.

5.2 Menggunakan Migrasi Versi

1. Memasang Alat Atlas

Sebelum menggunakan migrasi versi, Anda perlu memasang alat Atlas di sistem Anda. Atlas merupakan alat migrasi yang mendukung beberapa sistem basis data, menyediakan fitur yang powerful untuk mengelola perubahan skema basis data.

macOS + Linux

curl -sSf https://atlasgo.sh | sh

Homebrew

brew install ariga/tap/atlas

Docker

docker pull arigaio/atlas
docker run --rm arigaio/atlas --help

Windows

https://release.ariga.io/atlas/atlas-windows-amd64-latest.exe

2. Menghasilkan Berkas Migrasi Berdasarkan Definisi Entitas Saat Ini

atlas migrate diff nama_migrasi \
  --dir "file://ent/migrate/migrations" \
  --to "ent://ent/schema" \
  --dev-url "docker://mysql/8/ent"

3. Berkas Migrasi Aplikasi

Setelah berkas migrasi dihasilkan, mereka dapat diterapkan pada lingkungan pengembangan, pengujian, atau produksi. Biasanya, Anda pertama-tama akan menerapkan berkas migrasi ini ke basis data pengembangan atau pengujian untuk memastikan bahwa mereka dieksekusi seperti yang diharapkan. Kemudian, langkah-langkah migrasi yang sama akan dieksekusi di lingkungan produksi.

atlas migrate apply \
  --dir "file://ent/migrate/migrations" \
  --url "mysql://root:pass@localhost:3306/example"

Gunakan perintah atlas migrate apply, spesifikasikan direktori berkas migrasi (--dir) dan URL basis data target (--url) untuk menerapkan berkas migrasi.