Setelah memutuskan cara merender halaman, pertanyaan arsitektur berikutnya adalah bentuk keseluruhan aplikasi: satu kesatuan besar yang dikerjakan dan dideploy sebagai satu unit, atau kumpulan layanan kecil yang berkomunikasi lewat jaringan? Perdebatan monolith versus microservices sering dibingungkan oleh retorika — monolith dicitrakan warisan masa lalu, microservices dicitrakan kedewasaan teknis. Chapter ini membedah kedua bentuk tersebut dengan jujur: kekuatan, harga yang benar-benar dibayar, mitos yang menyertainya, dan posisi khas PHP yang ternyata hidup nyaman di tengah-tengah.
Dua Bentuk Aplikasi #
Monolith adalah aplikasi yang dikompilasi, dikerjakan, dan dideploy sebagai satu kesatuan: satu codebase, satu proses yang menangani request, satu basis data. Modul pembayaran, katalog, dan autentikasi hidup berdampingan di direktori yang sama dan saling memanggil sebagai pemanggilan fungsi atau method biasa. Skala horizontal tetap mungkin — sepuluh server menjalankan salinan aplikasi yang identik — tetapi unit penyebarannya tetap satu.
Microservices membalik setiap keputusan tersebut: aplikasi dipecah menjadi layanan-layanan kecil yang dideploy terpisah, memiliki basis data sendiri, dan berkomunikasi lewat jaringan — biasanya HTTP atau antrean pesan. Layanan pesanan tidak memanggil method class LayananPengiriman; layanan pesanan mengirim request HTTP ke server pengiriman. Pilihan ini mendahului pilihan framework: sebuah monolith bisa dibangun dengan Laravel maupun Slim, dan sekumpulan microservices bisa dibangun dari kedua framework yang sama.
Kekuatan dan Kelemahan Monolith #
| Aspek | Kondisi pada monolith |
|---|---|
| Pengembangan | Satu repository, satu IDE; refactoring lintas fitur sekadar pindah paket |
| Deployment | Satu artefak; tidak ada urutan layanan yang harus dijaga |
| Debugging | Satu stacktrace menembus seluruh aplikasi |
| Transaksi | Satu koneksi database; ACID berlaku penuh lintas fitur |
| Skala | Seluruh aplikasi diskalakan meski hanya satu modul yang sibuk |
| Keterikatan | Tanpa disiplin, modul saling import dan batas fitur memudar |
Dua baris terakhir tabel adalah sumber ketenaran buruk monolith, dan keduanya adil. Skala: bila 95% beban CPU ada di modul laporan, menambah server berarti ikut menambah kapasitas modul yang menganggur. Keterikatan: karena tidak ada tembok fisik antar modul, disiplin harus datang dari kesepakatan tim — dan kesepakatan tergerus oleh tenggat. Namun perhatikan bahwa empat baris pertama adalah keuntungan struktural yang tidak bisa dibeli oleh microservices dengan uang semurah apa pun: pemanggilan method adalah mekanisme komunikasi termurah, paling cepat, dan paling mudah di-debug yang pernah ada.
Janji dan Harga Microservices #
| Keuntungan yang dijanjikan | Harga yang harus dibayar |
|---|---|
| Scaling per layanan | Infrastruktur, observability, dan koordinasi deployment berlipat |
| Deploy independen antar tim | Kontrak API harus dinegosiasikan dan dijaga versinya |
| Isolasi kegagalan per layanan | Kegagalan jaringan menjadi mode gagal yang baru |
| Pilihan teknologi per layanan | Utilitas, standar, dan tooling terduplikasi |
| Tim kecil yang mandiri | Menuntut kematangan organisasi dan operasional tinggi |
Tabel di atas sengaja ditulis sebagai pasangan: setiap keuntungan datang bersama tagihannya pada baris yang sama. Isolasi kegagalan nyata — layanan katalog tumbang tidak menjatuhkan layanan pembayaran — tetapi jenis kegagalan baru muncul: layanan yang sehat namun tidak terjangkau karena jaringan. Deploy independen memang mempercepat tim, tetapi hanya setelah organisasi membangun kontrak API, versioning, dan pipeline yang mampu menopangnya.
Kompleksitas Terdistribusi yang Nyata #
Distribusi mengubah hal-hal yang tadinya gratis menjadi mahal. Panggilan method tidak pernah “timeout”; panggilan HTTP bisa — sehingga setiap klien layanan harus menangani timeout, retry dengan penundaan, dan penentu apakah operasi gagal sebelum atau sesudah efeknya terjadi. Retry yang terburu-buru menciptakan badai permintaan justru saat sistem sedang sekarat, dan operasi yang gagal di tengah harus aman diulang — syarat idempotency yang kita bahas di Idempotency .
Konsistensi data adalah tagihan terbesar. Pada monolith, satu transaksi database menjamin pesanan dan stok tercatat bersama atau sama-sama batal. Pada microservices dengan database terpisah, tidak ada transaksi lintas layanan — sistem harus hidup dengan eventual consistency: layanan pesanan mencatat pesanan, layanan stok menyesuaikan diri belakangan, dan selama jeda itu data keduanya tidak sejalan. Desain sagas, kompensasi, dan rekonsiliasi ada justru untuk mengelola jeda tersebut. Di atas semuanya berdiri beban observability: satu perjalanan request yang dulu satu stacktrace kini terpencar ke lima layanan, dan menyatukannya menuntut distributed tracing dengan correlation ID.
Mitos Ukuran #
Kata “micro” menjelaskan ukuran tiap layanan, bukan ukuran masalah yang diselesaikan. Aplikasi kecil yang dipecah menjadi sepuluh layanan tetap menyelesaikan masalah yang sama — kini dengan sepuluh deployment, sepuluh pipeline, dan sepuluh kemungkinan titik kegagalan. Kompleksitas bisnis tidak menyusut karena dipindahkan; kompleksitas tersebut hanya berubah wujud dari kode yang bisa dibaca menjadi topologi yang harus dipetakan.
Ukuran yang tepat untuk memecah adalah pertanyaan alasan, bukan angka. Alasan yang sah berbau organisasi dan skala: dua tim yang saling menghalangi pada satu codebase, satu modul yang beban trafiknya berbeda satu tingkat dari sisanya, atau batas domain yang memang sudah tegas. Bila alasan yang bisa disebut hanyalah “perusahaan besar memakai microservices”, pembagian tersebut menambah biaya tanpa menyelesaikan masalah apa pun yang nyata terjadi.
Realitas Aplikasi PHP #
Sebagian besar aplikasi PHP sukses di dunia adalah monolith, dan tidak ada yang salah dengan gambaran tersebut. WordPress menjalankan porsi terbesar web dalam satu codebase monolitik; aplikasi Laravel dan Symfony yang menopang bisnis nyata hampir selalu berbentuk monolith; dan sistem internal di perusahaan mana pun cenderung satu aplikasi dengan banyak modul. Monolith pada PHP bukan fase menunggu migrasi — bentuk tersebut adalah pilihan yang terbukti.
Model eksekusi PHP bahkan memberi monolith keunggulan khusus. Arsitektur shared-nothing membuat setiap request berjalan dalam ruang memori bersih, dan PHP-FPM menskalakan beban dengan menambah worker — yang berarti menskalakan monolith PHP sering kali cukup menambah proses dan server, tanpa memecah aplikasi. Opcode cache menyimpan hasil kompilasi agar request berikutnya murah, dan bottleneck yang tersisa hampir selalu di database — wilayah yang dijawab dengan indeks dan cache, bukan dengan membelah aplikasi. Untuk mayoritas beban kerja web, monolith PHP yang dirancang rapi sudah sangat mampu menskala.
Modular Monolith sebagai Jalan Tengah #
Antara “monolith yang memblending semua modul” dan “microservices penuh” terbentang jalan tengah yang semakin dijadikan arah utama: modular monolith. Aplikasi tetap satu codebase dan satu deployment, tetapi modul-modulnya dipisahkan dengan batas tegas — namespace sendiri, kontrak interface eksplisit, aturan dependensi yang ditegakkan — sehingga struktur internal menyerupai microservices tanpa biaya jaringannya. Bila suatu hari satu modul benar-benar perlu dipisah menjadi layanan, batas yang sudah tegas membuat pemisahan tersebut menjadi operasi kecil, bukan operasi bedah. Kita bedah bentuk ini tuntas pada chapter Modular Monolith .
Struktur Organisasi dan Hukum Conway #
Melvin Conway mengamati pada 1967 bahwa arsitektur sistem mencerminkan struktur komunikasi organisasi yang merancangnya — hukum Conway. Tim yang satu dan kompak akan menghasilkan aplikasi yang menyatu, apa pun arsitektur yang dicita-citakannya; organisasi yang timnya terpisah sekat departemen akan menghasilkan sistem yang terfragmentasi, sekuat apa pun usaha menyatukannya. Arsitektur aplikasi, pada akhirnya, adalah foto organisasi pembuatnya.
Konsekuensinya membalik urutan pertanyaan. Pertanyaan pembuka bukan “monolith atau microservices?” melainkan “tim seperti apa yang kita punya, dan seperti apa yang sedang kita bangun?”. Tiga developer yang membangun produk baru akan tenggelam menjaga delapan layanan; sebaliknya, organisasi dengan enam tim produk yang masing-masing memegang domain berbeda memang paling diuntungkan kepemilikan layanan terpisah. Memilih arsitektur tanpa memetakan struktur tim adalah memilih bangunan sebelum menghitung penghuninya.
Memeriksa Batas Modul dengan Kode #
Prasyarat modular monolith adalah batas modul yang tidak hanya dideklarasikan tetapi juga ditegakkan. Pada PHP, batas praktis dimulai dari namespace : setiap modul hidup di namespace sendiri, dan aturan dependensi menyatakan namespace mana yang boleh bergantung ke mana. Inti penegakannya bisa ditulis dalam PHP murni — daftar aturan, daftar dependensi nyata, dan pemeriksaan keduanya:
<?php
/**
* Pemeriksa batas modul: membandingkan dependensi nyata
* ($grafik) dengan aturan dependensi yang disepakati ($batas).
*/
$batas = [
'UI' => ['Application', 'Domain'],
'Application' => ['Domain'],
'Domain' => [],
'Infrastructure' => ['Domain'],
];
$grafik = [
'UI' => ['Application'],
'Application' => ['Domain', 'Infrastructure'],
'Domain' => ['UI'],
'Infrastructure' => ['Domain'],
];
foreach ($grafik as $dari => $tujuanTercatat) {
foreach ($tujuanTercatat as $ke) {
$diizinkan = in_array($ke, $batas[$dari] ?? [], true);
$status = $diizinkan ? 'OK' : 'PELANGGARAN';
echo "$dari -> $ke : $status\n";
}
}Output yang Dihasilkan:
UI -> Application : OK
Application -> Domain : OK
Application -> Infrastructure : PELANGGARAN
Domain -> UI : PELANGGARAN
Infrastructure -> Domain : OKArray $batas menyatakan aturan: lapisan UI boleh bergantung ke Application dan Domain, lapisan Domain tidak boleh bergantung ke siapa pun, dan seterusnya — aturan dependency rule yang sama dengan Layered Architecture
. Array $grafik menyatakan kenyataan: dependensi yang benar-benar terjadi antar modul, dalam praktiknya diambil dari analisis pernyataan use pada kode. Loop bersarang memeriksa setiap dependensi nyata terhadap daftar putih, dan in_array() dengan mode ketat true memastikan perbandingan persis. Dua pelanggaran tertangkap: Application -> Infrastructure melanggar karena lapisan aplikasi seharusnya tidak mengenal detail teknis, dan Domain -> UI adalah pelanggaran arah paling serius — domain inti tidak boleh tahu apa pun tentang antarmuka pengguna. Di proyek nyata, alat seperti Deptrac menjalankan pemeriksaan yang sama secara otomatis pada setiap commit, sehingga pelanggaran batas tertangkap sebelum menyatu menjadi spaghetti.
Ringkasan #
Monolith menang pada kesederhanaan pengembangan, debugging, dan transaksi; microservices menang pada kepemilikan tim dan skala per layanan — dan membayar semuanya dengan kompleksitas terdistribusi yang nyata: kegagalan jaringan, konsistensi akhir, dan beban operasional. Mayoritas aplikasi PHP hidup sebagai monolith dan berkembang baik di sana, sementara pilihan matang paling relevan bagi kita adalah modular monolith: batas tegas tanpa jaringan. Chapter berikutnya membahas bentuk tersebut sampai ke struktur folder dan penegakan aturannya.