Skip to content

Pindah Kategori Antar-Modul: Dua Tempat yang Diubah Sekalian

Adityo Guni Waluyo

Satu UPDATE terdengar selesai. Nyatanya, mapping importer harus ikut berubah, atau data lama akan bangkit kembali di import berikutnya.

Ringkasan

Gw kira mindahin kategori oleh-oleh Depok cuma butuh query UPDATE doang, ternyata bahaya kalo kodenya nggak diubah juga. Jadi gw bikin migrasi idempoten pake WHERE guard plus update moduleMap di importer go dalam satu commit biar nggak balik lagi. Soalnya DDL MySQL nggak bisa diandalkan pake transaksi, jadi guard ini yang jaga aman.

Saya buka ticket pagi itu. Permintaannya terlihat sepele, kategori "Oleh-oleh Khas Depok" harus dipindahkan dari modul EKONOMI_KREATIF ke PARIWISATA. Alasannya cukup masuk akal, Kerangka Acuan Kerja terbaru memang mengelompokkan suvenir daerah ini di bawah payung pariwisata.

Dulu, saya mungkin bakal langsung buka konsol database. Tinggal eksekusi satu baris query UPDATE, lalu anggap selesai. Ternyata nggak sesederhana itu.

Bahaya sebenarnya dari sebuah migrasi data pindah kategori antar modul bukan terletak pada eksekusi query-nya. Bahaya terbesar justru ada di sisi kode yang terus menghasilkan data tersebut. Bayangkan kalau saya cuma mengubah database tapi lupa memperbarui moduleMap di importer.go. Proses import data berikutnya akan dengan tenang menulis ulang nilai lama ke database. Data yang sudah saya perbaiki bakal hidup lagi dalam keadaan usang, menciptakan inkonsistensi yang sulit dilacak.

Dua tempat, satu commit

Saya memutuskan untuk mengubah keduanya dalam satu commit yang sama. Langkah pertama adalah menyiapkan file migrasi 000060_move_oleh_oleh_to_pariwisata.up.sql. Kuncinya ada di klausa WHERE.

UPDATE categories
SET module = 'PARIWISATA'
WHERE slug = 'oleh-oleh-khas-depok' AND module = 'EKONOMI_KREATIF';

Penambahan kondisi AND module = 'EKONOMI_KREATIF' ini bukan sekadar formalitas. Ini adalah mekanisme yang membuat query tersebut idempoten [4]. Jika migrasi ini dijalankan dua kali karena alasan tertentu, eksekusi kedua tidak akan mengubah apa pun karena kondisinya sudah tidak terpenuhi. Hal ini juga membuat down migration menjadi kebalikan yang presisi, sebuah praktik terbaik yang selalu saya terapkan agar reversibilitas terjaga [9].

Langkah kedua, saya membuka importer.go dan memperbarui moduleMap agar "Oleh-oleh Khas Depok" secara eksplisit memetakan ke PARIWISATA. Tentu saja, perubahan logika ini harus diikuti dengan penyesuaian di importer_test.go untuk memastikan mapping baru ini tertangkap oleh tes otomatis dan nggak merusak pipeline CI/CD.

Dengan menggabungkan perubahan migrasi dan pembaruan kode importer dalam satu commit, saya menghindari pertanyaan rumit tentang urutan deployment. Nggak perlu lagi khawatir apakah produksi menjalankan migrasi 000060 sebelum atau sesudah deployment kode importer, karena keduanya sudah terikat dalam satu perubahan atomik.

Saya pribadi lebih memilih pendekatan ini daripada sekadar mengandalkan skrip satu arah. Mengubah data tanpa mengubah sumber yang menghasilkan data itu ibarat mengepel lantai sambil keran air masih terbuka. Sinkronisasi antara skema database dan logika aplikasi adalah harga mati.

Kenapa bukan transaksi biasa

Perlu diingat juga bahwa perintah DDL di MySQL secara implisit mengakhiri transaksi aktif [10]. Jadi, membungkus migrasi ini dalam blok BEGIN dan COMMIT tidak akan melindungi kita jika ada kegagalan di tengah jalan. Mekanisme idempotensi lewat WHERE guard adalah lapisan pertahanan yang jauh lebih andal daripada mengandalkan transaksi database biasa untuk kasus seperti ini.

Sources:

Artikel terkait