Sistem Go hasil migrasi saya lahir dengan satu penyakit warisan. Semua repositori tidur dalam dua folder raksasa bernama repository dan usecase, campuran menjadi satu dari alert, billing, geofence, sampai fuel. Secara teknis tidak ada yang salah; secara mental, itu kota besar tanpa kecamatan. Mau cari rumah mana pun, kamu harus hafal seluruh kotanya.
Episode ini cerita merapikan kota itu, menjadi 14 modul domain, sambil endpoint-endpoint-nya terus melayani produksi. Karena, Anda tahulah, tidak ada tombol jeda untuk sistem yang dipakai puluhan ribu orang hahaha.
Kenapa Bukan Microservices #
Pertanyaan pertama yang biasanya muncul: kalau mau memisahkan domain, kenapa tidak langsung microservices?
Jawabannya: tim kami kecil, dan microservices itu bukan struktur arsitektur, dia keputusan infrastruktur. Tiap layanan berarti satu deployment baru, satu antrean monitoring baru, satu set masalah jaringan baru, plus komunikasi antar-layanan yang dulu gratis jadi mahal. Untuk ukuran tim kami, menukar folder yang rapi dengan enam server tambahan itu seperti membeli rumah untuk menyimpan kamar yang tidak terpakai.
Yang saya butuhkan bukan pemisahan proses, tapi pemisahan tanggung jawab. Itu nama nya: modular monolith, satu binary, satu deployment, tapi di dalamnya tiap domain punya rumah dengan dinding.
Modul Itu Rumah dengan Satu Pintu #
Konvensi modul di proyek ini sederhana:1 satu folder domain (misalnya geofence), di dalamnya repo dan usecase-nya; satu folder api yang jadi satu-satunya pintu keluar berisi interface; dan dinding yang dijaga compiler, bukan niat baik.
Dinding compile-time itu kuncinya. Kalau aturan modul cuma tertulis di README, aturan itu akan dilanggar di sprint sibuk, dijamin, pernah semua mengalaminya. Tapi kalau repo internal modul tidak bisa diimpor dari luar folder-nya, build langsung merah. Pelanggaran arsitektur jadi sesuatu yang tidak bisa di-commit, bukan sesuatu yang harus diingat-ingat hehe.
Sensus Dulu, Pindah Kemudian #
Bagian yang paling sering dilewatkan orang saat refactoring: menghitung dulu siapa memakai apa, sebelum memindahkan apa pun.
Untuk modul terbesar, device, saya membuat dokumen sensus sebelum menyentuh satu baris kode. Hasilnya mengejutkan sendiri: 51 file di luar modul mengonsumsi repo-nya, repo keluarga device menawarkan 321 method exported… dan yang benar-benar dipanggil dari luar cuma 100.2 Lebih dari dua pertiga stoknya tidak pernah diminta. Bahkan satu repositori “dewa” berisi 122 method, yang terpakai 49.
Sensusnya pun punya jebakan yang bikin saya tertawa saat mengalaminya. Menghitung konsumen pakai pencarian nama, dan ternyata akhiran “-device” menyebar di mana-mana: ada repo shared-link-device, ada user-group-device, keduanya bukan bagian keluarga device. Ada pula satu repo yang namanya mengandung angka, dan pola pencarian huruf-saja saya melewatinya tanpa sadar. Kesimpulannya rendah hati: sensu s yang tidak curiga pada dirinya sendiri akan menghitung dunia yang salah hehe~
Pindahan yang Byte-Identik #
Di sini prinsip dari episode keempat kembali berlaku, ternyata juga untuk rumah saya sendiri: mempindahkan bukan momen memperbaiki.
Aturan kerjanya: file repo dipindah ke modul tanpa mengubah satu byte pun, Git sampai mengakuinya sebagai pindahan 100% identik. Yang diubah hanya peta pintunya: interface di folder api, adapter yang meneruskannya, dan import path para konsumen. Kalau diff menunjukkan apa pun selain pindahan dan penggantian nama, itu tanda ada yang selingkuh dari rencana.
Kenapa seketat itu? Karena review pindahan byte-identik itu murah: yang dicek bukan “apakah logikanya benar” tapi “apakah benar tidak ada yang berubah”. Tiga puluh ribu baris berpindah rumah dengan pertanyaan review yang bisa dijawab dalam hitungan menit. Perbaikan kecil yang menggoda di tengah jalan, biasanya berupa “sekalian ganti nama variabel ini yuk”, ditunda semua. Satu PR, satu maksud.
Ragam Modul Itu Wajar #
Tidak semua modul dirapikan dengan resep sama. Modul geofence saya seal paling ketat: wrapper delegasi era lama dihapus, puluhan method mati dibersihkan, dan kode penanda error-nya diekspor lewat pintu api supaya konsumen bisa memeriksanya tanpa bobong ke dalam rumah. Modul sharing malah mencatat sejarah kecil: modul pertama yang butuh membaca config aplikasi di pintunya.
Sedangkan modul device, yang terbesar, saya sengaja biarkan pintunya terbuka untuk sementara: repos-nya masih bisa diimpor langsung, dengan rencana yang tertulis jelas untuk seal menyusul. Ini pengecualian sadar, seperti cerita parity di episode keempat: dogma itu untuk buku pelajaran; proyek hidup butuh pengecualian yang tercatat.
Cache Tanpa Redis #
Satu keputusan infrastruktur yang sering ditanyakan: cache-nya in-process, ristretto, bukan Redis. Sebagian karena kantor memang tidak menjalankan Redis dan tidak ada yang ingin menambah service baru hanya untuk cache; sebagian karena filosofi deployment saya: satu binary, geser, jalan. Cache ikut mati bersama prosesnya pun tidak masalah, dia bukan sumber kebenaran, cuma percepat. Kalau suatu hari skala menuntut cache bersama antar-instance, keputusan itu dibuka kembali dengan senang hati. Yang penting keputusannya punya alasan yang masih hidup, bukan warisan yang tidak siapa pun berani sentuh hahaha.
Utang yang Tersisa #
Supaya setia pada tradisi episode kelima, utangnya saya akui di sini juga: beberapa repos di dalam modul masih membaca tabel domain lain lewat SQL langsung, jejak era pra-modul yang belum sepenuhnya diubah jadi bacaan lewat pintu api. Dua modul lain belum ter-seal sama sekali. Semua tercatat, semua punya giliran. Kota tidak selesai direntap dalam satu malam hahaha.
Pelajaran #
- Sensus sebelum pindah. Angka konsumen dan method yang benar-benar dipakai itu yang menentukan bentuk pintu modul, bukan tebakan, bukan selera.
- Dinding yang dijaga compiler lebih kuat daripada kesepakatan verbal. Aturan arsitektur yang tidak bisa dilanggar tanpa build merah adalah satu-satunya aturan yang bertahan di sprint sibuk.
- Pindahan byte-identik membuat review murah. Pisahkan “memindahkan” dari “memperbaiki”, campurkannya, dan keduanya sama-sama jadi susah dicek.
- Pengecualian antar modul itu wajar, asal sadar dan tercatat. Seragam sempurna itu indah di diagram; proyek hidup butuh pintu yang kebijakannya jelas.
Episode berikutnya kita keluar sebentar dari dashboard dan masuk ke jalan raya: bagaimana paket-paket TCP dari tracker GPS berubah menjadi baris data yang rapi di database time-series. Sampai ketemu di sana.
Sekian. Salam.