Setiap beberapa bulan muncul tren baru: "perusahaan besar memakai microservices, jadi kita juga harus." Padahal sebagian besar aplikasi bisnis, termasuk sistem kasir, absensi, dan booking, berjalan baik sebagai satu aplikasi Laravel yang rapi. Artikel ini membantu Anda memutuskan berdasarkan kondisi tim dan beban nyata, bukan berdasarkan kesan modern.
Dua Istilah, Dijelaskan Tanpa Jargon
Monolith
Seluruh fitur berada dalam satu basis kode dan satu proses deploy. Modul pelanggan, produk, pembayaran, dan laporan berbagi database yang sama. Satu git push dan seluruh aplikasi diperbarui. Laravel standar adalah monolith.
Microservices
Aplikasi dipecah menjadi layanan-layanan kecil yang masing-masing punya kode, database, dan deploy sendiri, lalu saling berbicara lewat API atau antrean pesan. Layanan pembayaran bisa memakai bahasa dan skala berbeda dari layanan notifikasi.
Perbandingan yang Jujur
| Aspek | Monolith | Microservices |
|---|---|---|
| Kecepatan membangun awal | Cepat | Lambat (butuh infrastruktur dulu) |
| Debugging | Satu log, satu stack trace | Harus menelusuri request lintas layanan |
| Transaksi data | Mudah, satu database | Sulit, konsistensi harus dirancang |
| Skala per fitur | Seluruh aplikasi diskalakan bersama | Tiap layanan diskalakan sendiri |
| Biaya server | Rendah | Tinggi (banyak proses, gateway, monitoring) |
| Kebutuhan tim | Satu orang bisa menguasai semua | Beberapa tim dengan kontrak antarlayanan |
| Risiko utama | Kode berantakan seiring waktu | Kompleksitas operasional |
Kenapa Microservices Sering Menjadi Kesalahan Awal
Memecah layanan memindahkan kompleksitas dari kode ke jaringan. Panggilan fungsi yang dulu selesai dalam mikrodetik kini menjadi request HTTP yang bisa lambat, timeout, atau gagal sebagian. Anda perlu menangani retry, versi API, trace terdistribusi, dan deploy yang harus kompatibel satu sama lain.
Contoh sederhana. Di monolith, membuat pesanan lalu mengurangi stok cukup satu transaksi database:
DB::transaction(function () use ($data) {
$pesanan = Pesanan::create($data);
Produk::whereKey($data['produk_id'])->decrement('stok', $data['qty']);
});
Jika pesanan dan stok berada di dua layanan berbeda, tidak ada transaksi tunggal. Bila layanan stok gagal setelah pesanan tersimpan, Anda butuh mekanisme kompensasi (saga) untuk membatalkannya. Itu pekerjaan tambahan yang besar untuk manfaat yang belum tentu Anda perlukan.
Jalan Tengah: Modular Monolith
Untuk kebanyakan tim kecil, pilihan terbaik adalah tetap satu aplikasi tetapi dengan batas modul yang tegas. Kode dikelompokkan per domain, bukan per jenis file:
app/
Modules/
Pesanan/
Models/
Actions/
Http/Controllers/
Events/
Pembayaran/
Models/
Actions/
Http/Controllers/
Laporan/
...
Aturannya sederhana:
- Modul tidak boleh langsung membaca tabel modul lain. Komunikasi lewat kelas Action atau event publik.
- Satu modul punya kelas pintu masuk yang jelas, misalnya
BuatPesanan::jalankan(). - Gunakan event untuk efek samping lintas modul, bukan pemanggilan langsung.
// Modul Pembayaran mengabari yang lain tanpa saling ketergantungan
event(new PembayaranBerhasil($pesananId));
// Modul Pesanan mendengarkan
Event::listen(PembayaranBerhasil::class, TandaiPesananLunas::class);
Dengan pola ini Anda mendapat batas yang rapi hari ini, dan bila suatu saat modul tertentu memang harus dipisah, kode sudah terisolasi sehingga ekstraksinya jauh lebih mudah.
Sinyal Bahwa Sudah Waktunya Memecah
Pertimbangkan memisahkan sebuah modul menjadi layanan sendiri hanya jika beberapa kondisi ini benar sekaligus:
- Satu bagian punya kebutuhan skala yang sangat berbeda, misalnya pemrosesan gambar yang menghabiskan CPU sementara bagian lain ringan.
- Ada beberapa tim yang sering saling menghambat saat deploy karena berbagi satu kode.
- Bagian itu butuh teknologi berbeda yang tidak cocok dengan PHP.
- Anda sudah punya monitoring, CI/CD, dan kemampuan operasional untuk mengelola banyak layanan.
Jika jawabannya "belum", teruskan dengan monolith dan optimalkan lewat cache, queue, dan indeks database yang tepat.
Contoh Kasus: Aplikasi Kasir yang Tumbuh
Bayangkan aplikasi kasir untuk satu toko yang kemudian dipakai tiga puluh cabang. Di awal, semua fitur berada dalam satu aplikasi Laravel: transaksi, stok, pelanggan, dan laporan. Tiga masalah biasanya muncul berurutan, dan masing-masing punya obat yang berbeda.
Laporan membuat halaman kasir melambat
Query laporan bulanan yang berat berjalan pada database yang sama dengan transaksi. Obatnya bukan microservices. Pindahkan pembuatan laporan ke job queue, simpan hasilnya ke tabel ringkasan, dan tampilkan dari sana. Pemakai kasir tidak lagi menunggu query panjang.
Cetak struk dan kirim WhatsApp menahan respons
Efek samping seperti kirim notifikasi cukup dijalankan sebagai listener yang di-queue. Respons ke kasir selesai lebih dulu, dan pengiriman dilakukan di belakang layar. Tidak ada layanan baru yang perlu dibangun.
Cabang butuh sinkronisasi dengan sistem akuntansi lain
Di sinilah batas yang jelas menolong. Karena modul Pembayaran sudah punya Action dan event publik, Anda cukup menambahkan endpoint API read-only atau webhook di monolith yang sama. Bila suatu hari volume integrasi membengkak, modul itulah yang paling mudah dipisahkan karena ketergantungannya sudah terdokumentasi lewat event.
Perhatikan bahwa dalam ketiga kasus, masalah selesai dengan teknik yang sudah ada di Laravel. Microservices baru benar-benar masuk akal jika queue, cache, dan modularisasi sudah dipakai habis dan hambatan masih ada.
Biaya Tersembunyi yang Perlu Dihitung
- Infrastruktur: setiap layanan butuh proses, konfigurasi, dan sertifikatnya sendiri. Tagihan server cepat bertambah.
- Observabilitas: Anda butuh log terpusat dan trace antarlayanan, jika tidak, mencari penyebab error menjadi tebak-tebakan.
- Pengembangan lokal: menjalankan semua layanan di laptop memerlukan Docker Compose dan sumber daya yang cukup.
- Pengujian: test integrasi lintas layanan lebih lambat dan lebih rapuh daripada test di dalam satu aplikasi.
- Onboarding: anggota tim baru harus memahami banyak repositori sekaligus.
Jika biaya-biaya ini belum sebanding dengan masalah yang ingin diselesaikan, tetap pada monolith yang modular adalah keputusan teknis yang sehat, bukan kemunduran.
Langkah Pengambilan Keputusan
- Tim kurang dari sepuluh orang dan satu produk: modular monolith.
- Satu fitur berat (laporan, media, AI): jalankan sebagai job queue atau worker terpisah dulu, belum perlu layanan penuh.
- Butuh integrasi dengan sistem lain: sediakan API dari monolith, tidak perlu memecah seluruh aplikasi.
- Ragu: mulai monolith. Memecah monolith yang sehat jauh lebih mudah daripada menyatukan microservices yang salah potong.