Belajar Software Architect - Microservices Architecture
Episode 12 of 28

Belajar Software Architect - Microservices Architecture

Kapan microservices layak dibayar dan bagaimana memotongnya dengan benar: prinsip service boundary berbasis domain, komunikasi sinkron vs asinkron, saga pattern untuk transaksi lintas service, serta failure handling — dengan simulasi ekstraksi modul dari modular monolith kita

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Setelah di episode 11 kalian merancang performance & scalability — caching berlapis, mitigasi stampede, dan capacity planning flash sale — pada episode ini kita masuk bab yang paling sering diperdebatkan di industri: microservices.

Perhatikan posisi kita: bukan episode 3, melainkan episode 12 — setelah boundary modul (7), data ownership (8), kontrak API (9), security (10), dan scaling design (11). Itu disengaja. Microservices adalah evolusi dari disiplin-disiplin itu, bukan titik awal. Modular monolith kita sengaja dirancang agar ekstraksinya murah; sekarang kita pelajari kapan dan bagaimana mengeksekusinya.

Kapan Microservices Layak Dibayar

Microservices membayar biaya operasionalnya hanya jika kalian butuh apa yang hanya ia berikan:

Sinyal GoSinyal Not Yet
Tim >15 orang, bottleneck koordinasi rilis1-2 tim masih nyaman satu repo
Modul punya profil scale sangat bedaSemua scale seragam
Domain butuh deploy independence nyataRilis mingguan cukup
Platform engineering siap (CI/CD, tracing, mesh)Belum ada tim platform
Boundary bounded context stabil & terujiDomain masih bergerak cepat

Prasyarat teknis minimal — tanpa ini jangan mulai: CI/CD per service otomatis, distributed tracing end-to-end, centralized logging + metrics, on-call rotation matang, dan kontrak antar service dijaga test. Kompleksitas terdistribusi tanpa observability setara menerbangkan pesawat buta.

Important

Ingat pemicu revisi ADR-0002 kita: tim >15 developer atau traffic >5x proyeksi. Simulasi di episode ini mengasumsikan salah satu pemicu tercapai. Ekstraksi adalah keputusan bertahap per-modul — bukan big bang pecah semuanya.

Memotong Service Boundary

Aturan pemotongan sama persis dengan modul episode 7 — karena service hanyalah modul yang boundary-nya menjadi proses jaringan:

  • Potong per bounded context, bukan per layer teknis. ordering-service valid; database-service dan business-logic-service adalah distributed monolith.
  • Satu database per service — non-negotiable. Database bersama = coupling data terselubung dalam seragam microservices.
  • Pemilik tunggal — satu service, satu tim yang on-call. Shared ownership adalah shared blame.
  • Ukuran oleh domain, bukan baris kode — "micro" bukan tujuan; nano-services (puluhan service untuk fitur kecil) adalah biaya orkestrasi tanpa manfaat.

Simulasi: Ekstraksi Pertama Studi Kasus

Dari lima modul kita, mana yang diekstrak duluan? Analisis profilnya:

Analisis kandidat ekstraksi
catalog    : read-heavy, cache-friendly → TETAP di monolith
inventory  : hot saat flash sale, write-heavy,
             scaling profile beda jauh → EKSTRAK #1
payment    : compliance PCI, isolasi audit,
             rilis siklus sendiri → EKSTRAK #2
shipping   : integrasi kurir eksternal, latensi tak menentu
             → kandidat ketiga, belum mendesak
ordering   : jantung transaksi, butuh ACID lokal
             → tetap inti monolith (sekarang)

Inventory dipilih pertama karena mismatch scaling-nya paling tajam: seluruh aplikasi tidak perlu di-scale 10x hanya demi reservasi stok.

Komunikasi Antar Service

Setiap panggilan jaringan bisa gagal tiga cara (timeout, error, sukses-tapi-respon-hilang). Pilih gaya komunikasi secara sadar per aliran:

GayaCocok UntukHarga
Sync REST/gRPCButuh jawaban sekarang (validasi, query)Coupling availability & latency
Async eventSide-effect, fan-out, integrasiFinal consistency, tracing lebih sulit

Prinsip komposisinya sama dengan episode 8: sinkron hanya di jalur yang menuntut jawaban; sisanya event. Aturan praktis tambahan lintas service:

  • Batasi rantai panggilan sinkron maksimal dua hop (A→B→C). Lebih dari itu, latensi gabungan dan blast radius failure menjelma kaskade.
  • Setiap service wajib punya timeout eksplisit lebih kecil dari budget caller-nya.
  • Kontrak via OpenAPI/proto dijaga contract test (episode 9) — sekarang lintas repos.

Transaksi Lintas Service: Saga Pattern

Tanpa database bersama, tidak ada commit dua fase yang pragmatis. Saga mengganti satu transaksi terdistribusi dengan rangkaian transaksi lokal + aksi kompensasi bila ada yang gagal.

Alur order studi kasus kita sebagai saga orchestration:

100%

Dua gaya koordinasi:

  • Orchestration — satu coordinator (order saga) memerintah langkah demi langkah. Alur terbaca di satu tempat; coordinator jadi komponen penting.
  • Choreography — tiap service bereaksi ke event tetangganya. Decoupled total, tapi alur tersebar; di atas ±4 langkah sulit diaudit.

Heuristik pemilihan: choreography untuk rantai pendek sederhana, orchestration begitu ada percabangan kondisi dan kompensasi — persis kasus checkout kita.

Kunci implementasi yang membuat saga benar:

  1. Semua langkah idempotent — retry pasti terjadi; reserveStock dengan orderId sama harus aman dieksekusi ulang.
  2. Kompensasi, bukan rollback — uang yang sudah keluar dikembalikan lewat refund; stok dilepas lewat release. Saga tidak membatalkan masa lalu, ia menambalnya.
  3. State machine eksplisit — status saga tersimpan persisten (PENDING_STOCK, CHARGING, COMPENSATING), sehingga crash di tengah bisa dilanjutkan, bukan hilang.
  4. Outbox pattern (episode 8) di tiap langkah — perintah dan efeknya tak boleh terpisah oleh kegagalan publish.

Failure Handling Antar Service

Failure adalah mode operasi normal terdistribusi, bukan pengecualian:

Perlengkapan wajib tiap caller
Timeout      : batas tunggu eksplisit < budget caller
Retry        : idempotent + backoff + jitter, max 2-3 attempt
Circuit breaker : putus sirkuit saat error rate tinggi,
                  coba lagi setelah half-open window
Fallback     : jawaban cadangan (cached value, degraded mode)
Bulkhead     : pisahkan pool resource per dependensi

Detail tuning masing-masing pola ini — threshold breaker, jitter distribution, bulkhead sizing — kita kerjakan utuh di episode 16 tentang resilience. Di sini yang penting kesadarannya: tanpa kelima perlengkapan itu, satu service lambat akan meruntuhkan grafik panggilan seluruhnya.

Observability Menjadi Wajib

Di monolith, satu stack trace menjawab segalanya. Di services, satu request menyusuri banyak process — tanpa instrumen, kalian buta:

  • Correlation ID dihasilkan di edge, diteruskan di semua header/payload.
  • Distributed tracing (OpenTelemetry → Jaeger/Tempo) merekam span per hop.
  • Golden signals per service — latency, traffic, errors, saturation.
  • SLO antar service — inventory menjanjikan p99 ≤ 100 ms kepada ordering; pelanggarannya terlihat sebagai error budget burn, bukan debat subyektif.

Tip

Sebelum mengekstrak service pertama, pasang OpenTelemetry di monolith dan biasakan tim membaca trace lintas modul. Skill debugging-trace harus sudah otomatis sebelum jarak antar komponen melebar menjadi jaringan.

Kesalahan Umum

  • Distributed monolith — service dipotong per layer, database dibagi, tapi semua harus rilis bersama. Biaya microservices, manfaat nol.
  • Ekstraksi big bang — memecah semua modul sekaligus; risiko melipatgandakan dan rollback mustahil. Selalu satu modul per gelombang, stabilkan dulu.
  • Shared library god — paket common berisi DTO domain diimpor semua service; perubahannya menyentuh semuanya — coupling yang kabur dari jaringan.
  • Sync chain panjang — request menyusuri lima service sinkron; p99 gabungan = jumlah p99 semua hop, insiden satu hop menjadi insiden global.
  • Saga tanpa idempotency — retry menciptakan double-charge atau stok bocor; correctness scenario episode 5 dilanggar tepat saat flash sale.
  • Nano-services — 30 service untuk domain yang cukup 5; biaya operasional meledak tanpa tambahan kapabilitas.

Penutup

Inti yang harus dibawa pulang:

  • Microservices layak saat tim besar, profil scale berbeda, dan platform siap — cek pemicu revisi ADR, bukan tren.
  • Potongan service = bounded context + satu database + satu pemilik; ukurannya ditentukan domain.
  • Komunikasi: sinkron minimal (maks dua hop), async untuk sisanya, timeout budget turun-menurun.
  • Saga menggantikan transaksi terdistribusi: langkah idempotent + kompensasi + state machine + outbox; orchestration saat alur bercabang.
  • Failure handling (timeout/retry/breaker/fallback/bulkhead) dan observability adalah prasyarat, bukan pelengkap.
  • Simulasi kita: inventory diekstrak pertama karena mismatch scaling tertajam; payment menyusul demi PCI isolation.

Di episode 13 selanjutnya kita perdalam sisi async: event-driven architecture — Kafka secara konsep, delivery semantics, consumer group, event sourcing, dan bagaimana topik-topik studi kasus kita dirancang agar replayable. Sampai jumpa!