Microservices Architecture
17 October 2026 • 24 menit baca • Microservices Architecture, Microservices Architecture Design, Go Microservices, Rust Microservices, Laravel Microservices, Kubernetes Deployment, Database per Service, Redis Caching, RabbitMQ DLQ, Event-Driven Architecture, Distributed Systems
Microservices Architecture: Panduan Enterprise dari Go, Rust, Laravel, React, Next.js, Vue.js hingga Kubernetes, Redis, Multi-Database, dan Dead Letter Queue
Membangun Distributed Systems yang Scalable, Resilient, dan Siap untuk Kebutuhan Enterprise
Ketika aplikasi bisnis berkembang, kebutuhan teknologi biasanya ikut meningkat. Sistem harus menangani lebih banyak pengguna, transaksi, integrasi eksternal, proses asynchronous, dan kebutuhan ketersediaan layanan yang semakin tinggi.
Pada tahap tertentu, arsitektur aplikasi perlu dievaluasi agar perubahan pada satu kapabilitas bisnis tidak selalu memengaruhi seluruh sistem. Salah satu pendekatan yang dapat digunakan adalah Microservices Architecture, yaitu arsitektur yang menyusun aplikasi menjadi layanan-layanan kecil berdasarkan kapabilitas bisnis, dengan batas tanggung jawab dan deployment yang jelas.
Namun, membangun microservices bukan sekadar memisahkan backend menjadi banyak REST API. Arsitektur ini juga membutuhkan strategi kepemilikan data, komunikasi antarlayanan, konsistensi transaksi, pengelolaan kegagalan, keamanan, observability, dan infrastruktur yang mampu mengoperasikan banyak service secara konsisten.
Ethan Tech Digital mengedepankan pendekatan software engineering yang mempertimbangkan kebutuhan bisnis, domain boundaries, Clean Code, API contracts, event-driven processing, serta infrastruktur yang sesuai dengan karakteristik beban kerja.
Artikel ini membahas bagaimana microservices dapat dirancang untuk aplikasi enterprise, termasuk contoh implementasi menggunakan Go (Golang), Rust, PHP dengan Laravel, JavaScript melalui React, Next.js, dan Vue.js, serta integrasi multi-database, Redis, message broker, Dead Letter Queue (DLQ), dan Kubernetes.
1. Apa Itu Microservices Architecture?
Microservices Architecture adalah pendekatan pengembangan perangkat lunak yang membagi aplikasi menjadi layanan-layanan independen berdasarkan tanggung jawab atau kapabilitas bisnis tertentu.
Setiap service idealnya memiliki:
- Batas domain dan tanggung jawab bisnis yang jelas.
- Kontrak komunikasi yang terdefinisi, seperti REST API, gRPC, atau event.
- Kepemilikan terhadap data yang dikelolanya.
- Proses build, testing, deployment, dan versioning yang terkontrol.
- Monitoring, logging, dan penanganan error yang dapat diamati.
- Strategi scaling dan pemulihan kegagalan sesuai kebutuhannya.
Sebagai contoh, sistem enterprise dapat terdiri dari Identity Service, Order Service, Payment Service, Inventory Service, Notification Service, dan Reporting Service.
Setiap layanan dapat menggunakan teknologi yang paling sesuai dengan kebutuhan teknis dan kemampuan tim. Namun, penggunaan banyak bahasa pemrograman hanya masuk akal jika manfaatnya sebanding dengan kompleksitas tambahan dalam pengembangan, debugging, observability, dan maintenance.
2. Contoh Arsitektur Microservices untuk Enterprise Application
Bayangkan sebuah platform bisnis yang menyediakan portal pelanggan, aplikasi internal, pemrosesan transaksi, dan integrasi dengan sistem eksternal.
Arsitekturnya dapat dirancang sebagai berikut.
CUSTOMERS / INTERNAL USERS | React / Next.js / Vue.js | CDN / WAF / Ingress | API Gateway / BFF | +--------------------+--------------------+ | | | Identity Service Order Service Product Service (Go) (Go) (Laravel) | | | PostgreSQL PostgreSQL MySQL | | | +--------------------+--------------------+ | Event / Message Broker (RabbitMQ / Kafka) | +--------------+--------------+ | | | Payment Service Inventory Service Notification (Rust) (Go) (Laravel) | | | PostgreSQL MySQL PostgreSQL
Redis: Cache / Rate Limit / Short-lived State
Kubernetes: Deployments / Services / Jobs / Scaling
Observability: Logs / Metrics / Traces / Alerting
Diagram tersebut merupakan contoh konseptual. Teknologi dan pembagian service aktual perlu ditentukan berdasarkan domain bisnis, volume transaksi, kebutuhan keamanan, serta kemampuan tim.
Hal terpenting bukan jumlah service atau banyaknya teknologi, melainkan batas tanggung jawab yang jelas dan komunikasi yang dapat diandalkan.
3. Service Decomposition: Bagaimana Menentukan Batas Microservices?
Salah satu kesalahan terbesar dalam implementasi microservices adalah membagi aplikasi berdasarkan tabel database atau setiap fitur kecil tanpa memahami domain bisnis.
Pendekatan yang lebih tepat adalah mengidentifikasi business capabilities dan domain boundaries.
Sebagai contoh, sistem asuransi dapat memiliki kapabilitas:
- Policy Administration
- Underwriting
- Claims Management
- Reinsurance
- Finance
- Customer & Master Data
- Identity & Access Management
- Reporting & Portfolio Analytics
Namun, setiap kapabilitas tidak otomatis harus menjadi satu microservice. Beberapa proses yang memiliki aturan bisnis, siklus perubahan, dan kepemilikan data yang erat mungkin lebih baik berada dalam satu service.
Pertimbangkan tiga faktor utama:
Domain cohesion: apakah aturan bisnis dalam service saling berkaitan erat?
Change coupling: apakah perubahan pada satu area selalu memerlukan perubahan serentak pada area lainnya?
Operational independence: apakah suatu domain benar-benar membutuhkan deployment, scaling, atau isolasi kegagalan secara independen?
Service boundary yang baik mengurangi ketergantungan lintas layanan dan mencegah distributed monolith, yaitu sistem yang memiliki banyak service tetapi tetap harus dirilis dan dioperasikan secara bersamaan karena coupling yang tinggi.
4. Database per Service dan Strategi Multi-Database
Dalam arsitektur microservices, setiap service sebaiknya memiliki kendali atas data persisten yang menjadi tanggung jawabnya.
Pola ini dikenal sebagai Database per Service. Artinya, service lain tidak boleh langsung mengubah tabel internal milik service tersebut. Akses dilakukan melalui API atau kontrak komunikasi yang disepakati.
Pendekatan ini tidak mengharuskan satu server database fisik untuk setiap service. Beberapa service dapat menggunakan server database yang sama dengan database atau schema terpisah, kredensial berbeda, dan kontrol akses yang membatasi kepemilikan data. Pemisahan server fisik dapat dilakukan ketika kebutuhan isolasi, performa, atau keamanan membenarkannya. Referensi arsitektur database per service.
Contoh pembagian database
ServiceDatabaseTujuanIdentity ServicePostgreSQLPengguna, role, dan identitas aplikasiOrder ServicePostgreSQLPesanan, status, dan transaksi lokalProduct ServiceMySQLKatalog dan atribut produkPayment ServicePostgreSQLStatus pembayaran dan referensi transaksiNotification ServicePostgreSQLStatus pengiriman notifikasi dan retryReporting ServiceRead model / analytical databaseQuery agregasi dan laporan lintas domainRedisIn-memory data storeCache, rate limiting, dan state sementara
Pemilihan database di atas hanya contoh. Tidak ada keharusan bahwa microservices menggunakan beberapa jenis database. Satu teknologi relational database dapat memadai untuk seluruh service apabila kebutuhan datanya serupa.
Bagaimana jika sebuah halaman membutuhkan data dari banyak database?
Misalnya, halaman detail pesanan membutuhkan informasi order, nama pelanggan, status pembayaran, dan ringkasan produk.
Ada beberapa pendekatan:
- API Composition: backend mengakses beberapa service, lalu menggabungkan responsnya.
- Backend for Frontend (BFF): backend khusus untuk kebutuhan suatu frontend mengorkestrasi data yang diperlukan.
- CQRS dan Read Model: event dari beberapa service digunakan untuk membangun model baca yang telah dioptimalkan untuk kebutuhan query.
Jangan membuat satu frontend mengakses seluruh database secara langsung. Selain memperbesar risiko keamanan, pendekatan tersebut menghilangkan batas kepemilikan data dan membuat frontend terlalu bergantung pada implementasi internal service.
Konsekuensi dari database yang terpisah
Pemisahan data meningkatkan independensi service, tetapi membuat transaksi lintas service menjadi lebih sulit. Tidak ada satu transaksi ACID lokal yang otomatis mencakup semua database dan message broker.
Untuk proses yang melibatkan beberapa service, gunakan pola seperti Saga, Transactional Outbox, idempotent consumers, dan eventual consistency sesuai kebutuhan bisnis.
5. Komunikasi Antarservice: REST, gRPC, dan Event-Driven Architecture
Microservices dapat berkomunikasi melalui dua pendekatan utama.
Synchronous communication
REST atau gRPC digunakan ketika pemanggil membutuhkan respons langsung dari service lain.
Contoh:
- Frontend meminta daftar produk.
- Order Service memeriksa informasi produk yang diperlukan.
- API Gateway meneruskan request ke service tujuan.
Keuntungannya adalah alur request-response relatif mudah dipahami. Kekurangannya, rantai panggilan yang panjang dapat meningkatkan latency dan membuat kegagalan satu service memengaruhi pemanggilnya.
Gunakan timeout, connection pooling, retry yang terbatas, dan circuit breaker bila sesuai. Retry tidak boleh dilakukan secara membabi buta terhadap operasi yang tidak idempotent.
Asynchronous communication
Message broker seperti RabbitMQ atau Kafka dapat digunakan ketika proses tidak harus selesai dalam satu request.
Contohnya:
- Order Service menerbitkan
OrderCreated. - Payment Service memproses permintaan pembayaran.
- Inventory Service memperbarui reservasi stok.
- Notification Service mengirim notifikasi setelah status relevan tersedia.
Pola ini membantu memisahkan waktu pemrosesan antarservice, tetapi memperkenalkan tantangan baru: duplikasi pesan, urutan event, keterlambatan konsumen, dan pemulihan kegagalan.
Karena itu, asynchronous messaging harus dirancang dengan idempotensi, observability, kebijakan retry, serta prosedur penanganan pesan yang gagal.
6. Contoh Implementasi Backend dengan Go (Golang)
Go cocok dipertimbangkan untuk API services, integrasi sistem, worker, dan layanan yang membutuhkan concurrency. Bahasa ini menyediakan goroutines, channels, context cancellation, serta dukungan HTTP dan tooling yang relatif sederhana.
Contoh berikut menunjukkan endpoint Order Service dengan pemisahan handler dan business logic.
package main
import ( "encoding/json" "errors" "log" "net/http" "strings")
type CreateOrderRequest struct { CustomerID string `json:"customer_id"` ProductID string `json:"product_id"` Quantity int `json:"quantity"`}
type Order struct { ID string `json:"id"` CustomerID string `json:"customer_id"` ProductID string `json:"product_id"` Quantity int `json:"quantity"` Status string `json:"status"`}
type OrderService struct { // Repository dan publisher diinjeksikan // melalui constructor pada implementasi nyata.}
func (s *OrderService) Create( req CreateOrderRequest,) (Order, error) { if strings.TrimSpace(req.CustomerID) == "" { return Order{}, errors.New("customer_id is required") }
if strings.TrimSpace(req.ProductID) == "" { return Order{}, errors.New("product_id is required") }
if req.Quantity < 1 { return Order{}, errors.New("quantity must be positive") }
// Ilustrasi saja: ID, persistence, dan event // harus diimplementasikan secara nyata. return Order{ CustomerID: req.CustomerID, ProductID: req.ProductID, Quantity: req.Quantity, Status: "PENDING", }, nil}
func (s *OrderService) Handler( w http.ResponseWriter, r *http.Request,) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return }
r.Body = http.MaxBytesReader(w, r.Body, 1<<20) defer r.Body.Close()
var req CreateOrderRequest dec := json.NewDecoder(r.Body) dec.DisallowUnknownFields()
if err := dec.Decode(&req); err != nil { http.Error(w, "invalid request", http.StatusBadRequest) return }
order, err := s.Create(req) if err != nil { http.Error(w, err.Error(), http.StatusUnprocessableEntity) return }
w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) _ = json.NewEncoder(w).Encode(order)}
func main() { service := &OrderService{}
mux := http.NewServeMux() mux.HandleFunc("/orders", service.Handler)
log.Fatal(http.ListenAndServe(":8080", mux))}
Kode ini merupakan contoh edukasi. Service tersebut belum melakukan persistensi, autentikasi, otorisasi, pembuatan ID, atau publikasi event. Karena itu, status 202 Accepted di sini belum membuktikan bahwa suatu workflow asynchronous sudah tersimpan secara andal.
Pola yang perlu ditambahkan untuk production
- Repository yang terhubung ke database milik Order Service.
- Context dan timeout untuk operasi I/O.
- Authentication dan authorization.
- Validasi domain dan idempotency key.
- Database transaction serta transactional outbox.
- Structured logging, metrics, tracing, dan graceful shutdown.
- Unit test dan integration test.
Untuk service yang menerbitkan event, jangan menganggap bahwa menulis ke database kemudian mem-publish event secara terpisah sudah cukup andal. Kegagalan di antara dua operasi tersebut dapat menyebabkan data dan event tidak konsisten.
7. Contoh Implementasi Backend dengan Rust
Rust cocok untuk komponen yang membutuhkan kontrol tipe yang ketat, pengelolaan resource, dan pemanfaatan concurrency yang aman. Namun, Rust tidak otomatis lebih cepat untuk setiap workload; hasilnya tetap bergantung pada desain algoritma, I/O, database, dan pengujian performa.
Contoh domain logic berikut memisahkan validasi pesanan dari HTTP dan database.
#[derive(Debug)]pub struct CreateOrder { pub customer_id: String, pub product_id: String, pub quantity: u32,}
#[derive(Debug)]pub enum OrderError { MissingCustomer, MissingProduct, InvalidQuantity,}
#[derive(Debug)]pub struct Order { pub customer_id: String, pub product_id: String, pub quantity: u32, pub status: &'static str,}
pub fn create_order( input: CreateOrder,) -> Result<Order, OrderError> { if input.customer_id.trim().is_empty() { return Err(OrderError::MissingCustomer); }
if input.product_id.trim().is_empty() { return Err(OrderError::MissingProduct); }
if input.quantity == 0 { return Err(OrderError::InvalidQuantity); }
Ok(Order { customer_id: input.customer_id, product_id: input.product_id, quantity: input.quantity, status: "PENDING", })}
Result membuat hasil sukses dan error menjadi bagian dari kontrak fungsi. Business logic juga dapat diuji tanpa menjalankan web server atau database.
Pada aplikasi production, domain function tersebut dapat dihubungkan ke framework seperti Axum, kemudian ke repository database dan event publisher melalui abstraksi yang sesuai.
Contoh struktur proyek Rust
order-service/├── src/│ ├── main.rs│ ├── api/│ │ └── orders.rs│ ├── application/│ │ └── create_order.rs│ ├── domain/│ │ └── order.rs│ └── infrastructure/│ ├── postgres.rs│ └── messaging.rs├── tests/└── Cargo.toml
Struktur ini bersifat ilustratif. Proyek kecil tidak harus menggunakan banyak lapisan. Yang terpenting adalah memisahkan aturan bisnis dari detail transport, database, dan messaging ketika pemisahan tersebut memberikan manfaat nyata.
8. Contoh Implementasi dengan PHP dan Laravel
Laravel menyediakan routing, middleware, validation, dependency injection, database transactions, queue workers, serta berbagai komponen untuk membangun backend services.
Laravel dapat digunakan sebagai satu microservice yang memiliki domain jelas, misalnya Product Service, Notification Service, atau Admin API.
Contoh endpoint untuk Product Service
// routes/api.php
use App\Http\Controllers\ProductController;use Illuminate\Support\Facades\Route;
Route::get('/products/{id}', [ ProductController::class, 'show',]);
Controller:
<?php
namespace App\Http\Controllers;
use App\Models\Product;use Illuminate\Http\JsonResponse;
final class ProductController{ public function show(string $id): JsonResponse { $product = Product::query()->find($id);
if ($product === null) { return response()->json([ 'error' => 'PRODUCT_NOT_FOUND', ], 404); }
return response()->json([ 'data' => [ 'id' => $product->id, 'name' => $product->name, 'price' => $product->price, ], ]); }}
Model Product dan database connection dalam contoh ini adalah milik Product Service. Service lain sebaiknya mengambil informasi produk melalui API atau event yang disediakan, bukan membaca tabel products secara langsung.
Hal penting untuk Laravel dalam arsitektur microservices
- Gunakan Form Request untuk validasi request yang kompleks.
- Gunakan service layer atau use-case classes ketika business logic berkembang.
- Gunakan database transaction untuk menjaga konsistensi operasi lokal.
- Gunakan queue untuk pekerjaan asynchronous yang sesuai.
- Gunakan mekanisme idempotensi pada handler event dan operasi bisnis yang dapat dikirim ulang.
- Kelola konfigurasi dan secrets melalui environment atau secret management yang sesuai.
- Sediakan health endpoints, logging, metrics, dan graceful shutdown untuk kebutuhan operasional.
Laravel queue tidak otomatis menjadikan seluruh proses lintas service konsisten. Jika perubahan database dan publikasi event harus andal, gunakan transactional outbox atau mekanisme setara yang dirancang dengan cermat.
9. Frontend Microservices: React, Next.js, dan Vue.js
React, Next.js, dan Vue.js umumnya digunakan untuk membangun antarmuka pengguna, bukan menggantikan backend microservices.
Dalam arsitektur enterprise, frontend berkomunikasi dengan API Gateway atau Backend for Frontend (BFF), yang kemudian meneruskan permintaan ke service backend yang sesuai.
A. React
React dapat digunakan untuk membangun SPA atau komponen UI yang mengonsumsi API.
import { useEffect, useState } from "react";
export function ProductList() { const [products, setProducts] = useState([]); const [error, setError] = useState(null);
useEffect(() => { const controller = new AbortController();
async function loadProducts() { try { const response = await fetch("/api/products", { signal: controller.signal, });
if (!response.ok) { throw new Error("Failed to load products"); }
const result = await response.json(); setProducts(result.data); } catch (err) { if (err.name !== "AbortError") { setError("Unable to load products."); } } }
loadProducts();
return () => controller.abort(); }, []);
if (error) return <p>{error}</p>;
return ( <ul> {products.map((product) => ( <li key={product.id}>{product.name}</li> ))} </ul> );}
Contoh ini menggunakan endpoint relatif /api/products, yang diasumsikan diarahkan ke API Gateway atau BFF. Untuk aplikasi produksi, pertimbangkan pengelolaan server state, caching, autentikasi, retry terbatas, serta pembatalan request.
B. Next.js
Next.js dapat digunakan untuk membuat web application dengan server-side rendering, static rendering, dan server-side data access sesuai kebutuhan.
Contoh berikut menggunakan Server Component:
export default async function ProductsPage() { const response = await fetch( `${process.env.PRODUCT_SERVICE_URL}/products`, { cache: "no-store" } );
if (!response.ok) { throw new Error("Unable to load products"); }
const result = await response.json();
return ( <main> <h1>Products</h1>
<ul> {result.data.map((product) => ( <li key={product.id}> {product.name} </li> ))} </ul> </main> );}
Variabel PRODUCT_SERVICE_URL harus disediakan di environment server dan tidak menggunakan prefiks NEXT_PUBLIC_ jika nilainya merupakan konfigurasi internal yang tidak boleh diekspos ke browser.
Untuk production, server-side fetch harus dilengkapi timeout, autentikasi service-to-service bila diperlukan, error handling, dan strategi caching yang sesuai.
Next.js dapat berfungsi sebagai BFF untuk menggabungkan data beberapa service. Namun, hindari menjadikan route handler atau Server Action sebagai tempat untuk menduplikasi seluruh business logic backend.
C. Vue.js
Vue.js dapat digunakan untuk membangun SPA yang mengonsumsi endpoint API.
<script setup>import { onMounted, ref } from "vue";
const products = ref([]);const error = ref("");
onMounted(async () => { try { const response = await fetch("/api/products");
if (!response.ok) { throw new Error("Request failed"); }
const result = await response.json(); products.value = result.data; } catch { error.value = "Unable to load products."; }});</script>
<template> <section> <h1>Products</h1>
<p v-if="error">{{ error }}</p>
<ul v-else> <li v-for="product in products" :key="product.id" > {{ product.name }} </li> </ul> </section></template>
Untuk sistem yang lebih besar, logika API dapat dipisahkan ke service clients, composables, atau state management. Frontend tidak perlu mengetahui struktur database atau mengakses database service secara langsung.
Apakah setiap frontend harus menjadi microservice?
Tidak. React, Next.js, dan Vue.js adalah pilihan teknologi frontend. Microfrontend merupakan pendekatan berbeda yang membagi bagian antarmuka menjadi unit yang dapat dikembangkan atau dirilis secara independen.
Microfrontend dapat berguna pada organisasi dengan banyak tim frontend, tetapi menambah kompleksitas routing, shared dependencies, design systems, dan integrasi runtime. Untuk kebanyakan aplikasi, satu frontend modular yang mengonsumsi backend microservices sudah memadai.
10. Redis dalam Arsitektur Microservices
Redis dapat digunakan sebagai in-memory data store untuk berbagai kebutuhan operasional, tetapi bukan pengganti otomatis bagi database utama.
Penggunaan Redis perlu ditentukan berdasarkan karakteristik data, toleransi kehilangan data, konsistensi, dan kebutuhan ketersediaan.
A. Cache-aside
Alur umum:
- Service memeriksa apakah data tersedia di Redis.
- Jika cache hit, data dikembalikan.
- Jika cache miss, service membaca database miliknya.
- Hasilnya disimpan ke Redis dengan TTL yang sesuai.
Contoh konseptual:
GET product:123 | v Redis hit? -------- Yes ------> Return cached product | No | vProduct Service Database | vSet Redis key with TTL | vReturn product
Strategi invalidasi perlu dirancang ketika data berubah. TTL membantu membatasi usia cache, tetapi tidak menjamin bahwa data selalu mutakhir.
B. Rate limiting
Redis dapat menyimpan counter atau state untuk membatasi jumlah request per pengguna, API key, atau identitas lain.
Untuk rate limiting terdistribusi, operasi penghitung harus dirancang secara atomik, misalnya menggunakan Lua script atau mekanisme lain yang sesuai. Rate limit juga sebaiknya dilengkapi identitas yang benar dan tidak hanya berdasarkan IP apabila konteks autentikasi memungkinkan pendekatan yang lebih akurat.
C. Distributed coordination dan short-lived state
Redis dapat digunakan untuk distributed locks, token sementara, atau state workflow tertentu. Namun, distributed lock tidak boleh diperlakukan sebagai jaminan mutlak terhadap konsistensi transaksi bisnis. Gunakan database constraints, idempotency, fencing token, atau mekanisme lain jika correctness membutuhkan jaminan tambahan.
D. Redis dan database replication adalah hal berbeda
Redis cache bukan pengganti database read replica. Read replica biasanya digunakan untuk mendistribusikan beban pembacaan database, sedangkan Redis digunakan untuk akses data yang cepat dengan kebijakan cache atau pola penyimpanan yang sesuai.
Untuk data keuangan, pembayaran, atau status transaksi yang membutuhkan konsistensi ketat, database utama tetap menjadi sumber kebenaran kecuali sistem dirancang secara eksplisit dengan model lain.
11. Message Broker: RabbitMQ dan Kafka
Message broker menjadi komponen penting ketika proses bisnis memerlukan komunikasi asynchronous.
RabbitMQ sering digunakan untuk task queues dan routing pesan melalui exchange serta queue. Kafka sering digunakan untuk event streaming, log terdistribusi, dan pemrosesan event dalam volume besar.
Pemilihan broker tidak boleh hanya berdasarkan popularitas. Pertimbangkan kebutuhan delivery semantics, ordering, throughput, retention, replay, routing, dan kemampuan operasional tim.
Contoh event workflow
Order Service | | OrderCreated vMessage Broker | +------> Payment Consumer | +------> Inventory Consumer | +------> Notification Consumer
Setiap consumer menangani event sesuai tanggung jawabnya. Karena pengiriman ulang dapat terjadi, consumer perlu memproses event secara idempotent.
Contohnya, event pembayaran sebaiknya memiliki event_id atau idempotency key yang dapat digunakan untuk mendeteksi pesan yang sudah diproses.
Event envelope yang disarankan
{ "event_id": "evt-unique-id", "event_type": "OrderCreated", "event_version": 1, "occurred_at": "2026-10-16T10:00:00Z", "correlation_id": "request-or-workflow-id", "payload": { "order_id": "ord-123", "customer_id": "cus-456" }}
Contoh tersebut menunjukkan bentuk event yang dapat dikembangkan lebih lanjut. Identifier harus dibuat secara unik, timestamp harus memiliki semantik yang jelas, dan payload perlu divalidasi sesuai schema yang disepakati.
Hindari memasukkan data sensitif yang tidak dibutuhkan consumer. Perubahan schema sebaiknya mengikuti strategi versioning dan compatibility agar deployment producer serta consumer tidak selalu harus dilakukan bersamaan.
12. Dead Letter Queue (DLQ): Penanganan Pesan yang Gagal
Dead Letter Queue adalah mekanisme untuk menampung pesan yang tidak dapat diproses sesuai kebijakan yang telah ditentukan.
Misalnya, Payment Service menerima event OrderCreated, tetapi sistem pembayaran eksternal mengalami gangguan. Pesan dapat dicoba kembali sesuai retry policy. Jika jumlah percobaan sudah melewati batas, pesan dapat diarahkan ke DLQ untuk investigasi dan pemrosesan ulang yang terkontrol.
Contoh alur retry dan DLQ
Incoming Event | vMain Queue | vConsumer Processing | +---- Success ----> ACK / Complete | +---- Transient Error | | | v | Retry with Backoff | | | +---- Success ----> Complete | | | +---- Attempts Exceeded | | v vPermanent / Invalid Error Dead Letter Queue | v Alert + Investigation | v Controlled Redrive
Dalam RabbitMQ, Dead Letter Exchange (DLX) dapat digunakan untuk mengarahkan pesan dari queue ke exchange lain ketika pesan ditolak tanpa requeue, kedaluwarsa, melebihi batas panjang queue, atau mencapai delivery limit pada jenis queue yang mendukungnya. Detailnya bergantung pada versi RabbitMQ dan konfigurasi queue. Dokumentasi RabbitMQ DLX.
Contoh kebijakan retry
Jenis kegagalanTindakanTimeout sementaraRetry terbatas dengan exponential backoffHTTP 429 dari providerIkuti Retry-After jika tersediaHTTP 5xx dari providerRetry terbatas sesuai kebijakanPayload tidak validKarantina atau DLQ; jangan retry tanpa perubahanData bisnis tidak memenuhi aturanTandai sebagai business rejection; lakukan tindakan sesuai workflowBatas retry terlampauiDLQ, alert, dan investigasi
Tidak semua kegagalan bisnis perlu masuk DLQ. Misalnya, pembayaran ditolak karena saldo tidak cukup adalah hasil bisnis yang valid, bukan selalu kesalahan teknis yang harus diulang.
Apa yang harus disimpan untuk pesan DLQ?
Idealnya, pesan memiliki metadata seperti:
- Event ID dan correlation ID.
- Nama queue atau consumer asal.
- Waktu kegagalan.
- Jumlah percobaan.
- Kategori error.
- Versi schema.
- Identitas workflow atau transaksi yang terkait.
Akses ke DLQ harus dibatasi karena payload mungkin mengandung informasi sensitif. Tetapkan retention policy, alerting, audit trail, dan prosedur redrive yang idempotent. Redrive tidak boleh sekadar memindahkan semua pesan kembali ke antrean utama tanpa analisis penyebab kegagalan.
13. Transactional Outbox, Idempotency, dan Saga
Salah satu tantangan utama microservices adalah memastikan perubahan database dan publikasi event tetap konsisten.
Bayangkan Order Service berhasil menyimpan pesanan, tetapi gagal menerbitkan OrderCreated karena broker tidak tersedia. Database mencatat pesanan, tetapi service lain tidak pernah mengetahui bahwa pesanan dibuat.
Transactional Outbox membantu mengatasi masalah ini.
Cara kerja Outbox
Dalam satu transaksi database lokal:
- Order Service menyimpan pesanan.
- Order Service menulis record event ke tabel
outbox. - Transaksi di-commit.
- Background publisher membaca outbox dan menerbitkan event.
- Record ditandai telah dipublikasikan sesuai mekanisme yang dirancang.
Jika publisher mengalami kegagalan, event masih tersimpan untuk diproses kembali.
Pola ini mengurangi risiko kehilangan event antara commit database dan publish ke broker, tetapi publisher dan consumer tetap harus dirancang agar tahan terhadap pengiriman ulang. Referensi Transactional Outbox dan pola terkait.
Saga untuk transaksi lintas service
Misalnya, proses pembuatan pesanan melibatkan Order Service, Payment Service, dan Inventory Service.
Tidak tepat menganggap ketiga service dapat melakukan satu transaksi database lokal yang sama. Sebagai gantinya, proses dapat dirancang sebagai Saga yang terdiri dari serangkaian transaksi lokal.
Contoh:
- Order Service membuat order dengan status
PENDING. - Inventory Service mencoba melakukan reservasi stok.
- Payment Service memproses pembayaran.
- Jika seluruh langkah berhasil, order menjadi
CONFIRMED. - Jika langkah tertentu gagal, sistem menjalankan tindakan kompensasi yang sesuai.
Kompensasi bukan selalu rollback database secara otomatis. Misalnya, reservasi stok dapat dilepas, sementara pembayaran yang sudah diproses mungkin memerlukan refund sebagai transaksi bisnis terpisah.
Saga dapat menggunakan orchestration atau choreography. Pilih pendekatan berdasarkan kompleksitas workflow, kebutuhan visibilitas, dan kemudahan pemulihan. Referensi Saga Pattern.
14. Kubernetes untuk Deployment dan Scaling Microservices
Kubernetes menyediakan mekanisme untuk mengelola workload container, service discovery internal, rollout, dan scaling. Namun, Kubernetes tidak otomatis membuat aplikasi tahan kegagalan jika health checks, resource limits, shutdown handling, dan dependensi eksternal tidak dirancang dengan benar.
Komponen Kubernetes yang umum digunakan antara lain:
- Deployment: mengelola replica dan rollout aplikasi stateless.
- Service: menyediakan alamat jaringan stabil untuk mengakses Pod.
- Ingress atau Gateway API: mengatur akses HTTP dari luar cluster sesuai controller yang digunakan.
- ConfigMap dan Secret: menyediakan konfigurasi dan data sensitif melalui mekanisme yang sesuai.
- Horizontal Pod Autoscaler (HPA): menyesuaikan jumlah replica berdasarkan metrik yang dikonfigurasi.
- Job dan CronJob: menjalankan pekerjaan batch atau terjadwal.
- Readiness, Liveness, dan Startup Probes: membantu Kubernetes memahami kondisi aplikasi.
- NetworkPolicy: membatasi koneksi jaringan antar workload jika didukung konfigurasi cluster.
Contoh Kubernetes Deployment untuk Order Service
apiVersion: apps/v1kind: Deploymentmetadata: name: order-servicespec: replicas: 3 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: containers: - name: order-service image: registry.example.com/order-service:1.0.0 ports: - name: http containerPort: 8080
resources: requests: cpu: "100m" memory: "128Mi" limits: cpu: "500m" memory: "512Mi"
readinessProbe: httpGet: path: /ready port: http initialDelaySeconds: 5 periodSeconds: 10
livenessProbe: httpGet: path: /health port: http initialDelaySeconds: 10 periodSeconds: 20---apiVersion: v1kind: Servicemetadata: name: order-servicespec: selector: app: order-service ports: - name: http port: 80 targetPort: http type: ClusterIP
Contoh tersebut merupakan template awal. Image registry, health endpoints, resource allocation, security context, deployment strategy, dan konfigurasi jaringan perlu disesuaikan sebelum digunakan di production.
Endpoint /ready dan /health juga perlu memiliki semantik yang benar. Readiness sebaiknya menunjukkan apakah aplikasi siap menerima traffic; liveness sebaiknya menunjukkan apakah proses perlu direstart. Jangan membuat liveness probe gagal hanya karena satu dependensi eksternal sementara tidak tersedia, sebab hal itu dapat menyebabkan restart berulang.
Scaling tidak hanya berarti menambah replica
Penambahan Pod tidak otomatis meningkatkan throughput apabila bottleneck berada pada database, koneksi pool, message broker, atau layanan eksternal.
Karena itu, perencanaan scaling perlu mencakup:
- Capacity planning untuk CPU, memori, koneksi, dan storage.
- Database connection pooling dan query optimization.
- HPA berdasarkan metrik yang relevan.
- Pengendalian jumlah worker dan concurrency.
- Pod disruption budget dan strategi rollout jika diperlukan.
- Pengujian beban dan validasi target latency.
Stateful workloads seperti database dan broker membutuhkan pertimbangan operasional yang berbeda dari aplikasi stateless. Dalam banyak implementasi, database dan message broker dikelola menggunakan layanan terkelola atau platform khusus, sementara Kubernetes menjalankan aplikasi dan worker.
15. Security, Observability, dan Reliability
Arsitektur microservices memperluas permukaan komunikasi jaringan dan jumlah komponen yang perlu diamankan. Karena itu, keamanan harus diterapkan pada setiap service, bukan hanya di API Gateway.
Security
- Authentication dan authorization pada setiap batas yang relevan.
- Least privilege untuk identitas service dan kredensial database.
- TLS untuk komunikasi yang memerlukan perlindungan transport.
- Validasi input dan schema event.
- Secrets management dan rotasi kredensial.
- Audit trail untuk operasi sensitif.
- Network segmentation dan kebijakan akses antarlayanan.
- Proteksi data sensitif dalam log, cache, dan pesan.
Observability
Setiap request atau event penting sebaiknya dapat ditelusuri melalui correlation ID atau trace context yang konsisten.
Tiga pilar observability adalah:
- Logs: informasi kejadian dan konteks error.
- Metrics: latency, throughput, error rate, queue depth, dan penggunaan resource.
- Traces: perjalanan request lintas service dan identifikasi bottleneck.
Reliability
Setiap service perlu memiliki kebijakan timeout, retry, backoff, concurrency limits, serta penanganan overload. Gunakan circuit breaker bila tepat, dan pastikan fallback tidak menghasilkan data yang menyesatkan.
Untuk proses asynchronous, monitor umur pesan tertua, pertumbuhan queue, jumlah retry, jumlah pesan DLQ, dan waktu penyelesaian workflow.
Reliability tidak dapat diukur hanya dari jumlah replica. Ukur hasil berdasarkan SLI, SLO, dan kebutuhan bisnis yang disepakati.
16. Strategi Testing untuk Sistem Microservices
Pengujian harus mencakup perilaku service dan interaksi lintas service.
Unit testing: memverifikasi business logic tanpa infrastruktur eksternal.
Integration testing: menguji integrasi database, broker, cache, dan provider.
Contract testing: memverifikasi bahwa producer dan consumer mengikuti kontrak API atau event yang disepakati.
End-to-end testing: menguji alur bisnis dari awal hingga akhir, misalnya pembuatan order sampai status pembayaran diperbarui.
Resilience testing: menguji timeout, kegagalan dependensi, redelivery, backlog queue, dan pemulihan layanan.
Load testing: memvalidasi throughput, latency, dan perilaku sistem di bawah beban.
Pipeline CI/CD sebaiknya menjalankan pengujian dan pemeriksaan keamanan yang relevan sebelum artefak dirilis. Deployment bertahap dan rollback plan membantu mengurangi risiko saat perubahan mencapai production.
17. Kapan Perusahaan Perlu Menggunakan Microservices?
Microservices dapat menjadi pilihan yang baik apabila perusahaan membutuhkan deployment independen, batas domain yang jelas, scaling berbeda antar kapabilitas, atau isolasi operasional.
Namun, arsitektur ini juga membawa biaya tambahan: lebih banyak pipeline, kontrak, jaringan, monitoring, konfigurasi, dan skenario kegagalan.
Modular Monolith sering lebih tepat untuk aplikasi baru yang domainnya belum stabil, tim yang masih kecil, atau sistem yang belum membutuhkan independensi deployment.
Perusahaan dapat memulai dengan modular monolith, membangun batas domain yang jelas, lalu memisahkan service tertentu ketika terdapat kebutuhan bisnis atau teknis yang dapat diukur.
Keputusan arsitektur sebaiknya mempertimbangkan total cost of ownership, bukan sekadar tren teknologi.
18. Pendekatan Ethan Tech Digital dalam Merancang Microservices
Ethan Tech Digital memandang microservices sebagai keputusan arsitektur yang harus berangkat dari kebutuhan bisnis, bukan tujuan yang harus diterapkan pada setiap proyek.
Pendekatan pengembangan dapat meliputi:
- Requirements analysis dan pemetaan domain bisnis.
- Penentuan service boundaries dan ownership.
- Perancangan API contracts dan event schemas.
- Pemilihan database dan strategi data consistency.
- Penerapan Clean Code, SOLID, serta automated testing.
- Perancangan message processing, retry, idempotency, dan DLQ.
- Deployment menggunakan container dan Kubernetes sesuai kebutuhan.
- Implementasi observability, security, CI/CD, dan operational readiness.
- Evaluasi performa, kapasitas, biaya, dan kebutuhan scaling secara berkala.
Teknologi seperti Go, Rust, Laravel, React, Next.js, Vue.js, PostgreSQL, MySQL, Redis, RabbitMQ, Kafka, dan Kubernetes dapat digunakan sesuai peran serta kebutuhan masing-masing. Tidak semua proyek membutuhkan seluruh teknologi tersebut.
Tujuannya adalah merancang sistem yang memiliki batas tanggung jawab yang jelas, dapat dioperasikan secara andal, dan mendukung pertumbuhan bisnis tanpa menciptakan kompleksitas yang tidak perlu.
Kesimpulan
Microservices Architecture memberikan fleksibilitas untuk membangun dan mengembangkan kapabilitas bisnis secara independen. Namun, keberhasilannya bergantung pada kualitas service boundaries, kepemilikan data, komunikasi antarlayanan, strategi konsistensi, penanganan kegagalan, dan disiplin operasional.
Database per service membantu menjaga independensi data. Redis dapat mengoptimalkan akses data yang sesuai. Message broker dan DLQ membantu mengelola proses asynchronous. Transactional Outbox, idempotency, dan Saga mendukung keandalan workflow lintas service. Kubernetes menyediakan fondasi untuk deployment dan scaling containerized workloads.
Semua komponen tersebut harus dirancang sebagai satu sistem yang koheren, bukan sekadar dikumpulkan sebagai daftar teknologi.
Ethan Tech Digital — Building Maintainable, Secure & Scalable Digital Solutions.
Sedang merancang enterprise application, melakukan modernisasi sistem, atau mengevaluasi migrasi dari monolith ke microservices?
Diskusikan kebutuhan bisnis, service architecture, strategi integrasi, dan kesiapan infrastruktur bersama Ethan Tech Digital.
Butuh solusi digital untuk bisnis Anda?
Diskusikan Proyek →