Seluruh section ini membangun peralatan: batas service, REST dan messaging, retry, circuit breaker, idempotency, saga, event log, broker, registry, hingga tracing. Chapter penutup ini berdiri sebagai nota pembayarannya: setiap kemampuan yang microservices janjikan — rilis mandiri, skala per bagian, kegagalan terkurung — dibayar dengan kompleksitas yang permanen, dan sistem terdistribusi tidak punya tombol kembali ke kesederhanaan monolith. Keputusan yang sehat bukan keputusan teknis semata, melainkan penimbangan antara kebutuhan organisasi hari ini dan biaya operasional setiap hari sesudahnya. Kita tutup section dengan daftar harga yang jujur, neraca keputusan, dan jalur migrasi.
Daftar Harga #
Tabel berikut merangkum seluruh chapter sebelumnya dari sudut pandang pembeli — kemampuan yang dibeli, dan tagihan yang menyertainya:
| Kemampuan yang dibeli | Dibahas di | Harga permanennya |
|---|---|---|
| Rilis mandiri per tim | Service Boundaries | kontrak API + koordinasi versi antar tim |
| Skala per bagian | Monolith vs Microservices | deployment, provisioning, dan biaya per service |
| Kebebasan teknologi | Monolith vs Microservices | beberapa tumpukan yang harus dirawat |
| Kebebasan jaringan (messaging) | REST vs Messaging | idempotensi wajib + broker yang dioperasikan |
| Kegagalan terkurung | Resilience Patterns | timeout/retry/breaker/fallback di tiap jalur |
| Data milik sendiri | Distributed Transactions | tidak ada lagi JOIN dan transaksi ACID lintas batas |
| Alur panjang yang aman | Saga Pattern | aksi kompensasi dirancang, diuji, dipelihara |
| Audit dan pemutaran ulang | Event-Driven Architecture | skema event yang harus bertahan bertahun-tahun |
| Alamat yang selalu segar | Service Discovery | registry + health check sebagai infrastruktur |
| Jejak lintas proses | Observability dan Tracing | tiga pilar + disiplin log terstruktur |
Tidak ada baris yang bisa dihapus sebagian: “data milik sendiri” tanpa saga, atau “messaging” tanpa idempotency, menghasilkan sistem yang salah dengan cara yang sulit diperbaiki.
Neraca Keputusan #
Program berikut menilai enam faktor pada skala 0-3 untuk dua profil — startup kecil dan perusahaan multi-tim — lalu memetakan totalnya ke tiga zona rekomendasi:
import java.util.ArrayList;
import java.util.List;
/** Neraca trade-offs: skor kesiapan microservices untuk dua profil proyek. */
public class NeracaMikroservis {
/** Satu faktor keputusan dengan skor 0-3 per profil (3 = paling mendukung pemecahan). */
record Faktor(String nama, int skorProfilA, int skorProfilB) {}
static final List<Faktor> FAKTOR = List.of(
new Faktor("jumlah tim development yang terpisah", 0, 3),
new Faktor("kebutuhan rilis mandiri per area", 1, 3),
new Faktor("kebutuhan skala/teknologi berbeda per bagian", 1, 2),
new Faktor("kebutuhan blast radius kegagalan terkurung", 1, 3),
new Faktor("kematangan CI/CD dan observability tim", 2, 3),
new Faktor("keragaman domain yang perlu model sendiri", 1, 3));
public static void main(String[] args) {
System.out.println("== Neraca trade-offs (skala 0-3; 3 = paling mendukung pemecahan) ==");
System.out.println("profil-A = startup 4 orang, satu produk");
System.out.println("profil-B = perusahaan 3 tim lintas domain");
System.out.println();
int totalA = 0;
int totalB = 0;
int nomor = 1;
for (Faktor f : FAKTOR) {
System.out.println(" " + nomor + ". " + f.nama()
+ " : A=" + f.skorProfilA() + ", B=" + f.skorProfilB());
totalA += f.skorProfilA();
totalB += f.skorProfilB();
nomor++;
}
int maksimum = FAKTOR.size() * 3;
System.out.println();
System.out.println("== Hasil neraca ==");
System.out.println("profil-A: total " + totalA + "/" + maksimum
+ " -> " + rekomendasi(totalA, maksimum));
System.out.println("profil-B: total " + totalB + "/" + maksimum
+ " -> " + rekomendasi(totalB, maksimum));
}
/** Tiga zona keputusan berdasarkan rasio skor terhadap maksimum. */
static String rekomendasi(int total, int maksimum) {
int rasio = total * 100 / maksimum;
if (rasio >= 60) {
return "microservices masuk akal (" + rasio + " persen)";
}
if (rasio >= 40) {
return "monolith modular, siapkan batas domain (" + rasio + " persen)";
}
return "tetap monolith, fokus ke produk (" + rasio + " persen)";
}
}Output yang Dihasilkan:
== Neraca trade-offs (skala 0-3; 3 = paling mendukung pemecahan) ==
profil-A = startup 4 orang, satu produk
profil-B = perusahaan 3 tim lintas domain
1. jumlah tim development yang terpisah : A=0, B=3
2. kebutuhan rilis mandiri per area : A=1, B=3
3. kebutuhan skala/teknologi berbeda per bagian : A=1, B=2
4. kebutuhan blast radius kegagalan terkurung : A=1, B=3
5. kematangan CI/CD dan observability tim : A=2, B=3
6. keragaman domain yang perlu model sendiri : A=1, B=3
== Hasil neraca ==
profil-A: total 6/18 -> tetap monolith, fokus ke produk (33 persen)
profil-B: total 17/18 -> microservices masuk akal (94 persen)Mari kita bedah mekanisme penilaiannya:
record Faktor(nama, skorProfilA, skorProfilB): setiap faktor adalah pertanyaan penentu, bukan selera. “Jumlah tim development yang terpisah” diberi skor 0 untuk startup — pemecahan service membeli koordinasi lintas tim, dan tim tunggal tidak punya apa pun untuk dikoordinasikan.FAKTORsebagaiList.of(...): daftar faktor adalah data — menambah faktor baru tidak mengubah logika penilaian.rekomendasi(total, maksimum): tiga zona dipotong dari rasio skor — 60% ke atas berarti seluruh manfaat microservices benar-benar terbeli; 40-59% berarti batas domain disiplin lebih dulu (monolith modular); di bawah itu, energi lebih baik dihabiskan untuk produk.- Hasil dua profil: 6/18 versus 17/18 — dua angka berlawanan dari daftar faktor yang sama. Inilah inti chapter: keputusan arsitektur mengikuti konteks organisasi, dan kedua jawaban sama-sama benar untuk konteksnya.
Anti-Pattern Hasil Pemecahan Setengah Jalan #
Hasil pemecahan yang asal jadi punya nama yang patut dikenal. Distributed monolith: sistem dipecah menjadi banyak service yang tetap wajib dirilis serentak dan berbagi satu database — biaya terdistribusi dibayar penuh, manfaatnya tidak diterima. Nanoservices: service yang hanya menampung satu tabel atau satu endpoint — pergerakan jaringan bertambah tanpa manfaat. Sync chain: rantai panggilan REST berlapis (A → B → C → D) yang mengubah latensi jadi aditif dan kegagalan menular — persis jejak DuaGayaArsitektur di chapter pertama. Ketiganya berbagi satu akar: batas dipotong tanpa alasan bisnis yang jelas.
Jalur Migrasi yang Sehat #
Bila neraca memutuskan memecah, jalan yang aman tidak pernah dimulai dari nol. Pola strangler fig application menumbuhkan sistem baru di sekitar sistem lama: sebuah fasad (API gateway atau rute di tepi monolith) mencegat permintaan, mengarahkan yang sudah dimigrasikan ke service baru, dan meneruskan sisanya ke monolith — hingga monolith “tercekik” habis dan bisa dipensiunkan bertahap. Prasyaratnya dua: monolith punya tepi yang bisa dicegat (routing berbasis URL), dan data berpindah bertahap lewat sinkronisasi ganda.
Urutan ekstraksi yang lazim: mulai dari kapabilitas paling longgar koplingnya dan paling jarang berubah datanya (notifikasi, pelaporan), baru area inti (pesanan, pembayaran) yang menuntut saga penuh. Setiap ekstraksi adalah proyek kecil yang selesai — bukan perombakan besar tanpa rilis berbulan-bulan.
Dengan seluruh peralatan section ini di tangan — dan nota bayarannya sudah dibaca — kita siap membawa aplikasi Java ke fase berikutnya: dari kode yang berjalan menjadi sistem yang dikelola di produksi. Section Deployment dan Production membahas container, JVM di Kubernetes, profil rilis, dan semua hal yang terjadi setelah git push.
Referensi #
- Martin Fowler: Monolith First
- Martin Fowler: Strangler Application
- microservices.io: Strangler Application
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
NeracaMikroservis.java
Unduh berkasimport java.util.ArrayList;
import java.util.List;
/** Neraca trade-offs: skor kesiapan microservices untuk dua profil proyek. */
public class NeracaMikroservis {
/** Satu faktor keputusan dengan skor 0-3 per profil (3 = paling mendukung pemecahan). */
record Faktor(String nama, int skorProfilA, int skorProfilB) {}
static final List<Faktor> FAKTOR = List.of(
new Faktor("jumlah tim development yang terpisah", 0, 3),
new Faktor("kebutuhan rilis mandiri per area", 1, 3),
new Faktor("kebutuhan skala/teknologi berbeda per bagian", 1, 2),
new Faktor("kebutuhan blast radius kegagalan terkurung", 1, 3),
new Faktor("kematangan CI/CD dan observability tim", 2, 3),
new Faktor("keragaman domain yang perlu model sendiri", 1, 3));
public static void main(String[] args) {
System.out.println("== Neraca trade-offs (skala 0-3; 3 = paling mendukung pemecahan) ==");
System.out.println("profil-A = startup 4 orang, satu produk");
System.out.println("profil-B = perusahaan 3 tim lintas domain");
System.out.println();
int totalA = 0;
int totalB = 0;
int nomor = 1;
for (Faktor f : FAKTOR) {
System.out.println(" " + nomor + ". " + f.nama()
+ " : A=" + f.skorProfilA() + ", B=" + f.skorProfilB());
totalA += f.skorProfilA();
totalB += f.skorProfilB();
nomor++;
}
int maksimum = FAKTOR.size() * 3;
System.out.println();
System.out.println("== Hasil neraca ==");
System.out.println("profil-A: total " + totalA + "/" + maksimum
+ " -> " + rekomendasi(totalA, maksimum));
System.out.println("profil-B: total " + totalB + "/" + maksimum
+ " -> " + rekomendasi(totalB, maksimum));
}
/** Tiga zona keputusan berdasarkan rasio skor terhadap maksimum. */
static String rekomendasi(int total, int maksimum) {
int rasio = total * 100 / maksimum;
if (rasio >= 60) {
return "microservices masuk akal (" + rasio + " persen)";
}
if (rasio >= 40) {
return "monolith modular, siapkan batas domain (" + rasio + " persen)";
}
return "tetap monolith, fokus ke produk (" + rasio + " persen)";
}
}