Microservices vs Monolith: Kapan Harus Dipisah

Foto oleh Unsplash

Foto oleh Unsplash
Debat microservices vs monolith adalah salah satu keputusan arsitektur paling penting yang dibuat sebuah tim. Salah pilih dan Anda akan tenggelam dalam kompleksitas distributed system sebelum menemukan product-market fit, atau berjuang menskalakan monolith yang sudah menjadi bola kusut. Postingan ini memotong hype untuk memberi Anda kerangka dalam membuat pilihan yang tepat — dan strategi migrasi jika Anda perlu mengubah arah.
Sebuah monolith adalah unit deployable tunggal di mana semua modul — pengguna, pesanan, inventori, penagihan — berbagi proses yang sama dan sering berbagi database yang sama. Arsitektur microservices mengurai modul-modul tersebut menjadi layanan yang dapat di-deploy secara independen yang berkomunikasi melalui jaringan. Perbedaannya terdengar sederhana, tetapi memiliki implikasi mendalam untuk tim, tooling, dan beban operasional Anda.
Monolith mendapat reputasi buruk, tetapi monolith yang terstruktur dengan baik dengan batas modul yang jelas adalah titik awal yang sangat baik untuk hampir semua produk. Anda memiliki satu codebase untuk dinavigasi, transaksi yang mencakup beberapa modul ditangani secara sepele oleh database, debugging mudah (satu proses, satu log stream), dan deployment adalah satu artifact. Beban operasional minimal dibandingkan microservices.
Microservices memungkinkan deployment dan scaling independen per layanan, isolasi kegagalan, dan heterogenitas teknologi — layanan yang berbeda dapat menggunakan bahasa atau database yang berbeda. Tetapi setiap batas layanan yang Anda tambahkan menambah latensi jaringan, kebutuhan distributed tracing, tantangan eventual consistency, dan kompleksitas koordinasi deployment di berbagai repository dan tim.
// Monolith: single shared database, direct function calls
// src/orders/orderService.ts
import { UserRepository } from "../users/userRepository";
import { InventoryRepository } from "../inventory/inventoryRepository";
export class OrderService {
async placeOrder(userId: string, items: OrderItem[]): Promise<Order> {
const user = await UserRepository.findById(userId);
await InventoryRepository.reserveItems(items);
return OrderRepository.create({ userId, items, status: "pending" });
}
}
// Microservices: HTTP/message calls across service boundaries
// order-service/src/orderService.ts
export class OrderService {
async placeOrder(userId: string, items: OrderItem[]): Promise<Order> {
// Call user-service via HTTP
const user = await fetch(`http://user-service/users/${userId}`).then(r => r.json());
// Call inventory-service via message queue
await messageBroker.publish("inventory.reserve", { items, orderId: newId() });
return OrderRepository.create({ userId, items, status: "pending" });
}
}Buku 'Monolith to Microservices' dari Sam Newman merekomendasikan memulai dengan modular monolith — batas modul internal yang kuat yang diterapkan oleh kode, bukan network call. Ini memberi Anda pemisahan kepentingan tanpa overhead jaringan, dan membuat migrasi di masa depan jauh lebih mudah.
Pilihan yang tepat bergantung pada ukuran tim, frekuensi deployment, kebutuhan scaling, dan struktur organisasi Anda. Hukum Conway itu nyata: arsitektur sistem Anda akan mencerminkan struktur komunikasi tim Anda. Tim kecil yang membangun satu produk hampir selalu mendapat manfaat dari monolith. Organisasi besar dengan beberapa tim, SLA yang berbeda per layanan, dan kemampuan untuk memiliki pipeline deployment independen adalah rumah alami dari microservices.
Anda memiliki tim kecil (kurang dari 10 engineer), Anda masih mencari product-market fit dan perlu iterasi cepat, modul Anda berbagi data dan cross-cutting concern yang signifikan, Anda tidak memiliki tim DevOps atau platform khusus untuk mengelola infrastruktur layanan, atau Anda membangun alat internal atau MVP di mana kesederhanaan adalah yang utama.
Tim Anda berukuran dan terstruktur secara independen berdasarkan layanan, Anda perlu menskalakan komponen tertentu secara independen, layanan yang berbeda memiliki kebutuhan keandalan atau kepatuhan yang berbeda, Anda perlu mendukung beberapa lingkungan deployment secara independen, atau bagian sistem yang berbeda dimiliki oleh tim yang tidak boleh berbagi siklus deployment.
Jika Anda beralih dari monolith ke microservices, strangler fig pattern adalah pendekatan paling aman. Daripada penulisan ulang besar-besaran (yang hampir selalu gagal), Anda secara bertahap mengekstrak layanan dari monolith sambil tetap menjalankannya. API gateway merutekan request ke monolith atau microservice baru berdasarkan path atau feature flag. Seiring waktu, monolith menyusut saat layanan diekstrak.
Mulai dengan mengidentifikasi bounded context yang paling worth diekstrak — idealnya yang memiliki batas API yang jelas, kebutuhan scaling independen, dan tidak ada data coupling yang erat dengan monolith lainnya. Ekstrak data terlebih dahulu: beri layanan baru database-nya sendiri. Kemudian rutekan traffic melalui API gateway. Validasi layanan baru di bawah load produksi sebelum menghapus kode monolith.
Anti-pattern paling berbahaya adalah distributed monolith: Anda telah memecah aplikasi menjadi beberapa layanan, tetapi mereka sangat terikat — mereka berbagi database, saling memanggil secara sinkron dalam rantai panjang, dan harus di-deploy bersama. Anda mengambil semua kompleksitas operasional dari microservices tanpa manfaatnya. Sebelum mengekstrak sebuah layanan, pastikan ia benar-benar dapat memiliki data dan melakukan deploy secara independen.
Bagian tersulit dari migrasi microservices adalah data. Gunakan pola database-per-service: setiap layanan memiliki datanya sendiri dan hanya mengeksposnya melalui API-nya. Gunakan event sourcing atau change data capture (CDC) untuk menyinkronkan data antara monolith lama dan layanan baru selama periode transisi. Jangan pernah membiarkan dua layanan berbagi tabel database — itu menciptakan kembali coupling ketat yang ingin Anda hindari.
# Strangler Fig pattern: route by feature flag
# api-gateway/nginx.conf
location /api/users {
# New microservice handles /users
proxy_pass http://user-service:3001;
}
location /api/orders {
# Still monolith for now
proxy_pass http://monolith:3000;
}
location /api/inventory {
# Migrated to microservice
proxy_pass http://inventory-service:3002;
}Microservices mengalihkan kompleksitas dari kode Anda ke infrastruktur Anda. Anda membutuhkan service discovery, distributed tracing, centralized logging, health check, circuit breaker, dan API gateway sebelum arsitektur microservices Anda siap produksi. Anggarkanlah waktu untuk pekerjaan infrastruktur ini — ini tidak opsional.
Dalam monolith, Anda memiliki satu log stream. Dalam microservices, satu request pengguna dapat menyentuh sepuluh layanan. Distributed tracing (OpenTelemetry + Jaeger atau Tempo) memberi Anda visibilitas end-to-end. Structured logging dengan correlation ID memungkinkan Anda memfilter log di seluruh layanan untuk satu request. Metrik dan alerting per layanan (Prometheus + Grafana) memberi tahu Anda layanan mana yang menjadi bottleneck.
Service mesh seperti Istio atau Linkerd menangani mTLS antar layanan, load balancing, retry, timeout, dan circuit breaking di level infrastruktur — tanpa mengubah kode aplikasi. API gateway (Kong, AWS API Gateway) menangani autentikasi, rate limiting, dan routing di edge. Keduanya adalah force multiplier untuk arsitektur microservices tetapi menambah kompleksitas operasional tersendiri.
Gunakan health check dan readiness probe pada setiap layanan sejak hari pertama. Layanan yang gagal readiness probe-nya secara otomatis dihapus dari load balancer, mencegah cascading failure sebelum mencapai pengguna.
Keputusan monolith vs microservices tidak permanen, dan Anda tidak boleh memperlakukannya demikian. Mulai sederhana. Bangun monolith yang terstruktur dengan baik. Ketika bottleneck tertentu atau masalah kepemilikan tim benar-benar membutuhkan ekstraksi layanan, lakukan secara bedah, mengikuti strangler fig pattern. Hindari microservices-by-default dan dekomposisi prematur — keduanya adalah sumber kompleksitas yang tidak perlu yang paling umum dalam tim software modern.
Konsep arsitektur kunci yang dibahas di sini meliputi monolith, microservices, strangler fig, service mesh, and bounded context.