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 dibeliDibahas diHarga permanennya
Rilis mandiri per timService Boundarieskontrak API + koordinasi versi antar tim
Skala per bagianMonolith vs Microservicesdeployment, provisioning, dan biaya per service
Kebebasan teknologiMonolith vs Microservicesbeberapa tumpukan yang harus dirawat
Kebebasan jaringan (messaging)REST vs Messagingidempotensi wajib + broker yang dioperasikan
Kegagalan terkurungResilience Patternstimeout/retry/breaker/fallback di tiap jalur
Data milik sendiriDistributed Transactionstidak ada lagi JOIN dan transaksi ACID lintas batas
Alur panjang yang amanSaga Patternaksi kompensasi dirancang, diuji, dipelihara
Audit dan pemutaran ulangEvent-Driven Architectureskema event yang harus bertahan bertahun-tahun
Alamat yang selalu segarService Discoveryregistry + health check sebagai infrastruktur
Jejak lintas prosesObservability dan Tracingtiga 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:

text
== 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:

  1. 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.
  2. FAKTOR sebagai List.of(...): daftar faktor adalah data — menambah faktor baru tidak mengubah logika penilaian.
  3. 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.
  4. 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 #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

NeracaMikroservis.java

Unduh berkas
java
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)";
    }
}