Modular monolith sering lebih tepat daripada microservices ketika tim masih kecil sampai menengah, domain bisnis belum stabil, dan biaya koordinasi serta operasional perlu dijaga tetap rendah. Dalam banyak kasus, Anda bisa mendapatkan pemisahan modul, batas domain yang jelas, dan maintainability yang baik tanpa menambah kompleksitas jaringan, deployment terpisah, distributed tracing, sinkronisasi kontrak API, dan masalah konsistensi data lintas service.

Namun, ini bukan berarti microservices adalah pilihan yang salah. Microservices layak dipilih ketika kebutuhan isolasi skala, batas domain, kepemilikan tim, atau independensi deployment memang nyata dan terus-menerus menimbulkan bottleneck dalam monolith. Kuncinya bukan mengikuti tren arsitektur, melainkan memilih tingkat kompleksitas yang sesuai dengan ukuran tim, karakter beban kerja, dan toleransi operasional.

Memahami perbedaan: modular monolith vs microservices

Apa itu modular monolith?

Modular monolith adalah satu aplikasi yang dideploy sebagai satu unit, tetapi di dalamnya dibagi menjadi modul-modul dengan batas tanggung jawab yang jelas. Setiap modul idealnya memiliki:

  • API internal yang eksplisit
  • Aturan dependensi yang ketat
  • Logika domain yang terisolasi
  • Akses data yang dibatasi sesuai boundary modul

Secara fisik, aplikasi tetap satu proses atau satu deployment artifact. Secara logis, desainnya menyerupai sistem yang terstruktur berdasarkan domain.

Apa itu microservices?

Microservices memecah sistem menjadi service-service terpisah yang berjalan sebagai proses independen, umumnya dengan deployment, observability, scaling, dan kadang database masing-masing. Keuntungannya adalah isolasi yang lebih kuat, tetapi konsekuensinya adalah kompleksitas terdistribusi: komunikasi jaringan, retry, timeout, idempotency, konsistensi eventual, autentikasi antar-service, dan monitoring lintas komponen.

Perbedaan inti yang paling berdampak

Secara praktis, perbedaan terbesar bukan pada gaya coding, melainkan pada biaya koordinasi dan operasional. Pada modular monolith, panggilan antarmodul adalah panggilan in-process dan transaksi sering lebih sederhana. Pada microservices, panggilan lintas domain menjadi panggilan jaringan yang rentan latensi, kegagalan parsial, dan masalah sinkronisasi data.

Kapan modular monolith lebih tepat?

1. Tim masih kecil hingga menengah

Jika Anda memiliki beberapa engineer sampai belasan engineer, modular monolith sering memberi rasio manfaat terhadap kompleksitas yang lebih baik. Tim bisa fokus pada logika bisnis, bukan mengelola service discovery, message broker yang kompleks, deployment pipeline per service, atau distributed tracing yang belum tentu dibutuhkan sekarang.

Pada ukuran tim ini, bottleneck biasanya bukan karena satu aplikasi terlalu besar untuk dikelola, tetapi karena:

  • Boundary modul belum disiplin
  • Test suite lambat atau rapuh
  • Coupling terlalu tinggi
  • Review code dan ownership belum jelas

Masalah-masalah tersebut sering lebih efektif diperbaiki dengan modularisasi yang baik daripada memecah menjadi microservices terlalu dini.

2. Domain bisnis masih berkembang cepat

Saat product-market fit belum stabil atau workflow bisnis sering berubah, memecah sistem menjadi microservices terlalu awal bisa berisiko. Boundary yang hari ini terlihat masuk akal bisa berubah dalam beberapa bulan. Jika service sudah dipisah, perubahan domain yang seharusnya sederhana dapat berubah menjadi:

  • Perubahan kontrak API lintas service
  • Perubahan event schema
  • Data migration antar penyimpanan
  • Koordinasi deployment banyak komponen

Dalam fase ini, modular monolith lebih fleksibel karena refactor lintas modul masih dapat dilakukan dalam satu codebase dan satu deployment pipeline.

3. Deployment independen belum memberikan nilai nyata

Microservices sering dibenarkan dengan alasan setiap service bisa dideploy secara independen. Itu benar, tetapi hanya bernilai jika memang ada kebutuhan operasional dan organisasi untuk deploy secara terpisah. Bila mayoritas perubahan selalu menyentuh beberapa bagian sistem sekaligus, deployment independen belum tentu mengurangi risiko. Bahkan bisa menambah koordinasi karena setiap perubahan lintas service memerlukan kompatibilitas kontrak dan urutan rilis yang lebih hati-hati.

4. Beban kerja belum membutuhkan skala yang sangat berbeda

Jika hampir semua modul memiliki pola traffic yang serupa, Anda mungkin belum membutuhkan scaling per service. Dengan modular monolith, Anda masih bisa melakukan optimasi yang berarti:

  • Memperbaiki query database
  • Menambahkan cache
  • Memisahkan worker background dari web process
  • Mengoptimalkan queue dan job processing
  • Melakukan vertical scaling atau horizontal scaling pada aplikasi yang sama

Sering kali langkah-langkah ini cukup untuk fase pertumbuhan awal hingga menengah.

5. Observability dan operasional ingin tetap sederhana

Pada satu aplikasi modular, logging, metrics, profiling, dan debugging umumnya lebih mudah. Anda tidak perlu langsung menyiapkan korelasi trace lintas service, menangani log yang tersebar di banyak proses, atau mendiagnosis timeout yang hanya muncul saat beberapa service saling memanggil.

Untuk banyak tim, kesederhanaan ini berarti insiden lebih cepat dipahami dan diperbaiki.

Tabel perbandingan modular monolith dan microservices

AspekModular MonolithMicroservices
DeploymentSatu unit deployment, pipeline lebih sederhanaDeployment per service, lebih fleksibel tetapi lebih kompleks
Biaya operasionalLebih rendah untuk tim kecil-menengahLebih tinggi karena observability, network, dan orchestration
Kompleksitas komunikasiIn-process call, lebih mudah ditest dan ditraceNetwork call, perlu timeout, retry, circuit breaker, idempotency
TestingIntegration test lebih langsungPerlu contract test, integration test lintas service, environment lebih rumit
SkalabilitasScale aplikasi secara keseluruhan atau dengan optimasi terbatas per fungsiScale service tertentu secara lebih independen
Batas domainBisa jelas jika disiplin modularisasi diterapkanBoundary lebih kuat secara proses dan deployment
ObservabilityLebih sederhanaPerlu distributed tracing dan korelasi lintas service
Kecepatan delivery awalBiasanya lebih cepatBisa lebih lambat di awal karena setup platform dan koordinasi
Maintainability jangka panjangBaik jika modul konsisten dan coupling dijagaBaik jika domain matang dan tim ownership jelas; buruk jika fragmentasi berlebihan
Konsistensi dataLebih mudah, sering bisa pakai transaksi lokalLebih sulit, sering perlu eventual consistency dan desain event yang matang

Trade-off teknis yang sering diremehkan

Biaya operasional bukan sekadar biaya infrastruktur

Salah satu kesalahan umum adalah menganggap microservices hanya menambah beberapa container dan pipeline. Biaya sebenarnya juga muncul dalam bentuk:

  • Waktu engineer untuk mengelola CI/CD banyak service
  • Debugging insiden lintas service
  • Menjaga kompatibilitas API antar tim
  • Mengelola secret, autentikasi, dan otorisasi antar-service
  • Monitoring, alerting, dan tracing yang lebih detail

Jika tim platform atau DevOps belum kuat, kompleksitas ini cepat menjadi beban harian.

Testing pada sistem terdistribusi jauh lebih sulit

Pada modular monolith, Anda bisa menulis unit test per modul dan integration test yang menjalankan aplikasi dalam satu proses. Pada microservices, Anda perlu memikirkan:

  • Contract test untuk memastikan kompatibilitas API
  • Test event-driven flow
  • Test idempotency dan retry
  • Test terhadap kegagalan parsial dan timeout
  • Test environment yang menyerupai produksi

Tanpa disiplin testing yang kuat, microservices mudah terlihat rapi di diagram tetapi rapuh di runtime.

Boundary yang salah akan tetap salah, meski dipisah jadi service

Microservices bukan solusi otomatis untuk desain domain yang buruk. Jika batas domain masih kabur, memecah sistem hanya memindahkan coupling dari function call menjadi HTTP call atau message queue. Hasilnya justru lebih sulit dipelihara karena coupling tetap ada, tetapi sekarang tersembunyi di jaringan dan event schema.

Contoh nyata: e-commerce dan SaaS B2B

Kasus e-commerce

Bayangkan sistem e-commerce dengan modul:

  • Katalog produk
  • Keranjang belanja
  • Checkout
  • Pembayaran
  • Inventori
  • Promosi
  • Order management

Untuk tim kecil hingga menengah, pendekatan yang sering efektif adalah modular monolith dengan boundary domain yang jelas. Misalnya:

  • Modul catalog mengelola produk dan pencarian dasar
  • Modul cart mengelola state keranjang
  • Modul checkout mengorkestrasi proses order
  • Modul payment menjadi adapter ke payment gateway
  • Modul inventory mengelola stok dan reservasi

Selama beban traffic belum sangat timpang dan tim belum dibagi menjadi squad domain yang benar-benar otonom, desain ini biasanya cukup. Jika nanti modul catalog search membutuhkan skala, storage, atau indeks yang sangat berbeda, bagian itu bisa diekstrak lebih dulu tanpa memecah semua sistem sekaligus.

Kasus SaaS B2B

Pada SaaS B2B, modul umum biasanya meliputi:

  • Tenant management
  • Billing
  • User and access management
  • Workflow bisnis utama
  • Reporting
  • Notification

Sering kali workflow inti, billing, dan manajemen akses sangat terhubung. Jika dipisah terlalu dini, perubahan sederhana seperti model subscription baru dapat menyentuh banyak service, event, dan migrasi. Modular monolith lebih cocok selama domain masih berevolusi cepat dan tim produk butuh iterasi mingguan yang stabil.

Microservices mulai masuk akal ketika, misalnya, reporting atau notification memiliki pola skala, SLA, atau teknologi yang berbeda secara signifikan dari sistem inti.

Contoh implementasi boundary dalam modular monolith

Modular monolith yang baik bukan sekadar menaruh file dalam folder berbeda. Boundary perlu dipaksa melalui aturan dependensi dan API internal yang jelas.

src/
  modules/
    orders/
      application/
      domain/
      infrastructure/
      api/
    billing/
      application/
      domain/
      infrastructure/
      api/
    inventory/
      application/
      domain/
      infrastructure/
      api/
  shared/

Prinsip praktisnya:

  • Modul lain hanya boleh mengakses api/ atau facade yang diekspos
  • Jangan membaca tabel modul lain secara langsung jika bisa dihindari
  • Pindahkan integrasi lintas modul melalui event internal atau application service
  • Definisikan ownership yang jelas untuk model dan aturan bisnis

Contoh pseudo-code sederhana untuk komunikasi antarmodul:

// orders/api/PlaceOrder.ts
export interface PlaceOrderCommand {
  customerId: string;
  items: Array<{ productId: string; quantity: number }>;
}

export interface PlaceOrderResult {
  orderId: string;
  status: 'PENDING' | 'CONFIRMED';
}

// inventory/api/ReserveStock.ts
export interface ReserveStockRequest {
  orderId: string;
  items: Array<{ productId: string; quantity: number }>;
}

export interface ReserveStockResponse {
  reserved: boolean;
  failedItems?: string[];
}

Poin penting dari contoh ini bukan bahasanya, tetapi pola desainnya: interaksi terjadi melalui kontrak internal yang eksplisit, bukan melalui akses bebas ke detail implementasi modul lain. Dengan cara ini, jika nanti modul tertentu perlu diekstrak menjadi service terpisah, jalur migrasinya lebih bersih.

Kapan microservices layak dipilih?

1. Ada kebutuhan isolasi skala yang jelas dan berulang

Jika satu domain memiliki pola traffic, konsumsi resource, atau SLA yang sangat berbeda, pemisahan service bisa masuk akal. Contoh: search, recommendation, media processing, atau notification pipeline dengan throughput tinggi.

2. Tim sudah terbagi berdasarkan domain dan ownership stabil

Microservices bekerja lebih baik jika ada tim yang benar-benar memiliki service tertentu end-to-end: pengembangan, operasi, observability, dan kualitasnya. Jika semua engineer tetap harus memahami semua service untuk setiap perubahan, manfaat isolasi organisasi belum tercapai.

3. Batas domain sudah matang

Jika domain sudah cukup stabil dan interaksi antar-bagian sistem dipahami dengan baik, biaya pemisahan akan lebih terkendali. Ini penting agar service tidak saling memanggil terlalu sering atau berbagi data secara tidak sehat.

4. Kebutuhan deployment independen benar-benar penting

Misalnya, satu service perlu dirilis jauh lebih sering, memiliki SLA berbeda, atau harus dipelihara tanpa memengaruhi area lain. Dalam kondisi seperti ini, independensi deployment bisa memberi manfaat nyata.

5. Organisasi siap dengan platform dan observability

Jika Anda sudah memiliki fondasi seperti:

  • CI/CD yang matang
  • Centralized logging
  • Metrics dan alerting
  • Distributed tracing
  • Standar autentikasi dan komunikasi antar-service
  • Strategi contract testing

maka risiko operasional microservices menjadi lebih terkendali.

Indikator teknis dan organisasi untuk beralih dari modular monolith

Berikut sinyal yang lebih sehat untuk dipakai sebagai indikator transisi daripada sekadar “codebase mulai besar”.

Indikator teknis

  • Satu modul membutuhkan pola scaling yang sangat berbeda dan terus-menerus
  • Waktu build, test, atau startup aplikasi menjadi bottleneck serius yang tidak membaik dengan optimasi biasa
  • Kegagalan pada satu area sering mengganggu seluruh aplikasi dan isolasi proses diperlukan
  • Kebutuhan teknologi pada modul tertentu sangat berbeda, misalnya pipeline event atau komputasi berat
  • Boundary internal sudah stabil dan kontraknya jelas selama periode yang cukup lama

Indikator organisasi

  • Tim sudah dibagi berdasarkan domain, bukan lapisan teknis semata
  • Ownership service bisa dijaga tanpa banyak tumpang tindih
  • Ada kemampuan operasional untuk menjalankan banyak service secara konsisten
  • Proses rilis lintas domain sering saling menghambat dan deployment independen memang akan mengurangi antrean koordinasi

Jika alasan utamanya hanya karena “monolith susah dirawat”, periksa dulu apakah masalah sebenarnya adalah kurangnya modularisasi, test yang lemah, atau coupling yang tidak dikendalikan. Memecah deployment belum tentu memperbaiki akar masalah desain.

Sinyal anti-pattern: jangan buru-buru memilih microservices

  • Tim belum punya monitoring dan alerting yang konsisten untuk satu aplikasi, tetapi ingin mengelola banyak service
  • Boundary domain masih sering berubah setiap sprint
  • Semua service masih harus berbagi database yang sama tanpa aturan jelas
  • Perubahan fitur hampir selalu menyentuh banyak service sekaligus
  • Engineer belum nyaman dengan timeout, retry, idempotency, dan kegagalan parsial
  • Microservices dipilih hanya karena organisasi ingin terlihat modern atau mengikuti arsitektur perusahaan lain
  • Masalah utama sebenarnya ada pada query lambat, desain schema buruk, atau kurangnya caching

Anti-pattern juga bisa terjadi pada modular monolith, misalnya:

  • Modul hanya berupa folder, tetapi semua kode bebas saling mengakses
  • Semua modul membaca tabel semua modul lain
  • Service layer menjadi tempat campuran semua domain
  • Tidak ada aturan dependensi atau review terhadap boundary

Jika ini yang terjadi, monolith memang akan terasa berantakan. Solusinya adalah memperbaiki modularitas, bukan otomatis beralih ke distributed system.

Panduan migrasi bertahap: jangan pecah semua sekaligus

Jika Anda saat ini memiliki modular monolith dan mulai melihat kebutuhan isolasi nyata, pendekatan paling aman biasanya adalah ekstraksi bertahap. Langkah umum yang sering berhasil:

  1. Perjelas boundary modul di dalam monolith lebih dulu
  2. Pastikan komunikasi terjadi melalui kontrak internal yang eksplisit
  3. Kurangi akses langsung ke data milik modul lain
  4. Tambahkan event internal untuk alur yang memang asinkron
  5. Identifikasi satu modul dengan kebutuhan isolasi paling nyata
  6. Ekstrak modul tersebut sebagai service pertama, bukan seluruh domain sekaligus

Pilihan kandidat awal yang baik biasanya adalah komponen yang:

  • Punya interaksi relatif jelas
  • Tidak terlalu sentral ke semua transaksi sinkron
  • Memiliki kebutuhan skala atau operasional yang berbeda

Contohnya: notification, search indexing, document processing, atau reporting pipeline.

Checklist keputusan

Pilih modular monolith jika mayoritas jawaban Anda “ya”

  • Tim engineering masih kecil hingga menengah
  • Domain bisnis masih sering berubah
  • Anda ingin delivery cepat dengan operasional sesederhana mungkin
  • Kebutuhan scaling per domain belum terlalu berbeda
  • Deployment independen belum memberi manfaat nyata
  • Observability dan platform engineering masih terbatas
  • Anda masih bisa menjaga boundary modul dengan disiplin desain

Pilih microservices jika mayoritas jawaban Anda “ya”

  • Ada domain dengan kebutuhan skala, SLA, atau resource yang sangat berbeda
  • Tim sudah punya ownership domain yang stabil
  • Boundary domain cukup matang
  • Independensi deployment akan mengurangi bottleneck organisasi
  • Tim siap menghadapi distributed tracing, contract testing, dan kegagalan parsial
  • Platform operasional untuk banyak service sudah memadai

Kesimpulan

Modular monolith lebih tepat daripada microservices ketika Anda ingin menjaga arsitektur tetap terstruktur tanpa membayar biaya kompleksitas sistem terdistribusi terlalu dini. Untuk tim kecil hingga menengah, ini sering menjadi pilihan yang lebih pragmatis: delivery lebih cepat, debugging lebih sederhana, testing lebih langsung, dan maintainability tetap baik selama boundary modul dijaga dengan disiplin.

Microservices menjadi layak ketika kebutuhan isolasi domain, skala, deployment, dan ownership tim benar-benar nyata, bukan asumsi. Karena itu, keputusan terbaik bukan memilih arsitektur yang paling populer, melainkan yang paling sesuai dengan domain, ukuran tim, toleransi operasional, dan arah pertumbuhan sistem Anda.