[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-arsitektur-event-driven-rust-pola-bus-sistem-real-time":3},{"article":4,"author":57},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":7,"meta_description":16,"focus_keyword":17,"og_image":18,"canonical_url":18,"robots_meta":19,"created_at":15,"updated_at":15,"tags":20,"category_name":34,"related_articles":35},"d2000000-0000-0000-0000-000000000121","a0000000-0000-0000-0000-000000000026","Arsitektur Event-Driven di Rust — Pola Bus untuk Sistem Real-Time","arsitektur-event-driven-rust-pola-bus-sistem-real-time","Membangun sistem event-driven di Rust: pola event bus dengan tokio broadcast, decoupling komponen, dan arsitektur real-time untuk aplikasi backend.","## Apa Itu Arsitektur Event-Driven?\n\nArsitektur event-driven adalah pola di mana komponen berkomunikasi melalui event daripada panggilan langsung. Ini memungkinkan loose coupling — setiap komponen tidak perlu mengetahui komponen lain, hanya jenis event yang mereka terbitkan atau konsumsi.\n\nDalam Rust, kita membangun event bus menggunakan channel async dari tokio. Ini memberikan performa tinggi dengan zero-cost abstraction yang menjadi ciri khas Rust.\n\n## Event Bus dengan tokio::broadcast\n\n```rust\nuse tokio::sync::broadcast;\nuse serde::{Serialize, Deserialize};\n\n#[derive(Clone, Debug, Serialize, Deserialize)]\nenum AppEvent {\n    UserRegistered { user_id: Uuid, email: String },\n    OrderPlaced { order_id: Uuid, user_id: Uuid, total: f64 },\n    PaymentReceived { order_id: Uuid, amount: f64 },\n    InventoryUpdated { product_id: Uuid, quantity: i32 },\n}\n\nstruct EventBus {\n    sender: broadcast::Sender\u003CAppEvent>,\n}\n\nimpl EventBus {\n    fn new(capacity: usize) -> Self {\n        let (sender, _) = broadcast::channel(capacity);\n        Self { sender }\n    }\n    \n    fn publish(&self, event: AppEvent) {\n        \u002F\u002F Abaikan error jika tidak ada subscriber\n        let _ = self.sender.send(event);\n    }\n    \n    fn subscribe(&self) -> broadcast::Receiver\u003CAppEvent> {\n        self.sender.subscribe()\n    }\n}\n```\n\n## Subscriber Pattern\n\nSetiap komponen subscribe ke event yang relevan:\n\n```rust\nasync fn email_service(mut rx: broadcast::Receiver\u003CAppEvent>) {\n    loop {\n        match rx.recv().await {\n            Ok(AppEvent::UserRegistered { email, .. }) => {\n                send_welcome_email(&email).await;\n            }\n            Ok(AppEvent::OrderPlaced { order_id, user_id, .. }) => {\n                send_order_confirmation(user_id, order_id).await;\n            }\n            Ok(_) => {} \u002F\u002F Abaikan event lain\n            Err(broadcast::error::RecvError::Lagged(n)) => {\n                tracing::warn!(\"Email service tertinggal {} event\", n);\n            }\n            Err(broadcast::error::RecvError::Closed) => break,\n        }\n    }\n}\n\nasync fn inventory_service(mut rx: broadcast::Receiver\u003CAppEvent>) {\n    loop {\n        match rx.recv().await {\n            Ok(AppEvent::OrderPlaced { order_id, .. }) => {\n                reserve_inventory(order_id).await;\n            }\n            Ok(AppEvent::PaymentReceived { order_id, .. }) => {\n                confirm_inventory_reservation(order_id).await;\n            }\n            Ok(_) => {}\n            Err(broadcast::error::RecvError::Closed) => break,\n            Err(_) => continue,\n        }\n    }\n}\n```\n\n## Integrasi dengan Axum\n\n```rust\nuse axum::{Router, Extension, Json};\n\n#[derive(Clone)]\nstruct AppState {\n    event_bus: Arc\u003CEventBus>,\n    db: PgPool,\n}\n\nasync fn create_order(\n    State(state): State\u003CAppState>,\n    Json(payload): Json\u003CCreateOrderRequest>,\n) -> Result\u003CJson\u003COrder>, AppError> {\n    \u002F\u002F 1. Simpan ke database\n    let order = sqlx::query_as::\u003C_, Order>(\n        \"INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING *\"\n    )\n    .bind(payload.user_id)\n    .bind(payload.total)\n    .fetch_one(&state.db)\n    .await?;\n    \n    \u002F\u002F 2. Publish event\n    state.event_bus.publish(AppEvent::OrderPlaced {\n        order_id: order.id,\n        user_id: order.user_id,\n        total: order.total,\n    });\n    \n    \u002F\u002F 3. Return response segera\n    Ok(Json(order))\n}\n\n#[tokio::main]\nasync fn main() {\n    let event_bus = Arc::new(EventBus::new(1000));\n    \n    \u002F\u002F Spawn subscriber services\n    tokio::spawn(email_service(event_bus.subscribe()));\n    tokio::spawn(inventory_service(event_bus.subscribe()));\n    tokio::spawn(analytics_service(event_bus.subscribe()));\n    \n    let state = AppState {\n        event_bus,\n        db: create_pool().await,\n    };\n    \n    let app = Router::new()\n        .route(\"\u002Forders\", post(create_order))\n        .with_state(state);\n    \n    let listener = tokio::net::TcpListener::bind(\"0.0.0.0:3001\").await.unwrap();\n    axum::serve(listener, app).await.unwrap();\n}\n```\n\n## Keuntungan Event-Driven\n\n1. **Loose coupling** — Handler tidak perlu tahu tentang email, inventory, atau analytics service\n2. **Extensibility** — Tambahkan subscriber baru tanpa mengubah publisher\n3. **Testability** — Test setiap subscriber secara independen\n4. **Performance** — Event diproses secara asinkron, tidak memblokir response\n5. **Resilience** — Kegagalan satu subscriber tidak memengaruhi yang lain\n\n## Error Handling dan Retry\n\n```rust\nasync fn resilient_subscriber(\n    mut rx: broadcast::Receiver\u003CAppEvent>,\n    max_retries: u32,\n) {\n    loop {\n        let event = match rx.recv().await {\n            Ok(e) => e,\n            Err(_) => continue,\n        };\n        \n        for attempt in 0..max_retries {\n            match process_event(&event).await {\n                Ok(()) => break,\n                Err(e) if attempt \u003C max_retries - 1 => {\n                    tracing::warn!(\n                        \"Retry {}\u002F{}: {:?}\",\n                        attempt + 1, max_retries, e\n                    );\n                    tokio::time::sleep(\n                        Duration::from_millis(100 * 2u64.pow(attempt))\n                    ).await;\n                }\n                Err(e) => {\n                    tracing::error!(\"Gagal setelah {} retry: {:?}\", max_retries, e);\n                    \u002F\u002F Kirim ke dead letter queue\n                    dead_letter_queue(&event, &e).await;\n                }\n            }\n        }\n    }\n}\n```\n\n## Kesimpulan\n\nArsitektur event-driven di Rust memberikan decoupling, extensibility, dan performa tinggi. tokio broadcast channel menyediakan primitif yang efisien untuk event bus, dan integrasi dengan Axum memungkinkan HTTP handler yang responsif tanpa memblokir pada side effect.","\u003Ch2 id=\"apa-itu-arsitektur-event-driven\">Apa Itu Arsitektur Event-Driven?\u003C\u002Fh2>\n\u003Cp>Arsitektur event-driven adalah pola di mana komponen berkomunikasi melalui event daripada panggilan langsung. Ini memungkinkan loose coupling — setiap komponen tidak perlu mengetahui komponen lain, hanya jenis event yang mereka terbitkan atau konsumsi.\u003C\u002Fp>\n\u003Cp>Dalam Rust, kita membangun event bus menggunakan channel async dari tokio. Ini memberikan performa tinggi dengan zero-cost abstraction yang menjadi ciri khas Rust.\u003C\u002Fp>\n\u003Ch2 id=\"event-bus-dengan-tokio-broadcast\">Event Bus dengan tokio::broadcast\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">use tokio::sync::broadcast;\nuse serde::{Serialize, Deserialize};\n\n#[derive(Clone, Debug, Serialize, Deserialize)]\nenum AppEvent {\n    UserRegistered { user_id: Uuid, email: String },\n    OrderPlaced { order_id: Uuid, user_id: Uuid, total: f64 },\n    PaymentReceived { order_id: Uuid, amount: f64 },\n    InventoryUpdated { product_id: Uuid, quantity: i32 },\n}\n\nstruct EventBus {\n    sender: broadcast::Sender&lt;AppEvent&gt;,\n}\n\nimpl EventBus {\n    fn new(capacity: usize) -&gt; Self {\n        let (sender, _) = broadcast::channel(capacity);\n        Self { sender }\n    }\n    \n    fn publish(&amp;self, event: AppEvent) {\n        \u002F\u002F Abaikan error jika tidak ada subscriber\n        let _ = self.sender.send(event);\n    }\n    \n    fn subscribe(&amp;self) -&gt; broadcast::Receiver&lt;AppEvent&gt; {\n        self.sender.subscribe()\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"subscriber-pattern\">Subscriber Pattern\u003C\u002Fh2>\n\u003Cp>Setiap komponen subscribe ke event yang relevan:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">async fn email_service(mut rx: broadcast::Receiver&lt;AppEvent&gt;) {\n    loop {\n        match rx.recv().await {\n            Ok(AppEvent::UserRegistered { email, .. }) =&gt; {\n                send_welcome_email(&amp;email).await;\n            }\n            Ok(AppEvent::OrderPlaced { order_id, user_id, .. }) =&gt; {\n                send_order_confirmation(user_id, order_id).await;\n            }\n            Ok(_) =&gt; {} \u002F\u002F Abaikan event lain\n            Err(broadcast::error::RecvError::Lagged(n)) =&gt; {\n                tracing::warn!(\"Email service tertinggal {} event\", n);\n            }\n            Err(broadcast::error::RecvError::Closed) =&gt; break,\n        }\n    }\n}\n\nasync fn inventory_service(mut rx: broadcast::Receiver&lt;AppEvent&gt;) {\n    loop {\n        match rx.recv().await {\n            Ok(AppEvent::OrderPlaced { order_id, .. }) =&gt; {\n                reserve_inventory(order_id).await;\n            }\n            Ok(AppEvent::PaymentReceived { order_id, .. }) =&gt; {\n                confirm_inventory_reservation(order_id).await;\n            }\n            Ok(_) =&gt; {}\n            Err(broadcast::error::RecvError::Closed) =&gt; break,\n            Err(_) =&gt; continue,\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"integrasi-dengan-axum\">Integrasi dengan Axum\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">use axum::{Router, Extension, Json};\n\n#[derive(Clone)]\nstruct AppState {\n    event_bus: Arc&lt;EventBus&gt;,\n    db: PgPool,\n}\n\nasync fn create_order(\n    State(state): State&lt;AppState&gt;,\n    Json(payload): Json&lt;CreateOrderRequest&gt;,\n) -&gt; Result&lt;Json&lt;Order&gt;, AppError&gt; {\n    \u002F\u002F 1. Simpan ke database\n    let order = sqlx::query_as::&lt;_, Order&gt;(\n        \"INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING *\"\n    )\n    .bind(payload.user_id)\n    .bind(payload.total)\n    .fetch_one(&amp;state.db)\n    .await?;\n    \n    \u002F\u002F 2. Publish event\n    state.event_bus.publish(AppEvent::OrderPlaced {\n        order_id: order.id,\n        user_id: order.user_id,\n        total: order.total,\n    });\n    \n    \u002F\u002F 3. Return response segera\n    Ok(Json(order))\n}\n\n#[tokio::main]\nasync fn main() {\n    let event_bus = Arc::new(EventBus::new(1000));\n    \n    \u002F\u002F Spawn subscriber services\n    tokio::spawn(email_service(event_bus.subscribe()));\n    tokio::spawn(inventory_service(event_bus.subscribe()));\n    tokio::spawn(analytics_service(event_bus.subscribe()));\n    \n    let state = AppState {\n        event_bus,\n        db: create_pool().await,\n    };\n    \n    let app = Router::new()\n        .route(\"\u002Forders\", post(create_order))\n        .with_state(state);\n    \n    let listener = tokio::net::TcpListener::bind(\"0.0.0.0:3001\").await.unwrap();\n    axum::serve(listener, app).await.unwrap();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"keuntungan-event-driven\">Keuntungan Event-Driven\u003C\u002Fh2>\n\u003Col>\n\u003Cli>\u003Cstrong>Loose coupling\u003C\u002Fstrong> — Handler tidak perlu tahu tentang email, inventory, atau analytics service\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Extensibility\u003C\u002Fstrong> — Tambahkan subscriber baru tanpa mengubah publisher\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Testability\u003C\u002Fstrong> — Test setiap subscriber secara independen\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Performance\u003C\u002Fstrong> — Event diproses secara asinkron, tidak memblokir response\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Resilience\u003C\u002Fstrong> — Kegagalan satu subscriber tidak memengaruhi yang lain\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"error-handling-dan-retry\">Error Handling dan Retry\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">async fn resilient_subscriber(\n    mut rx: broadcast::Receiver&lt;AppEvent&gt;,\n    max_retries: u32,\n) {\n    loop {\n        let event = match rx.recv().await {\n            Ok(e) =&gt; e,\n            Err(_) =&gt; continue,\n        };\n        \n        for attempt in 0..max_retries {\n            match process_event(&amp;event).await {\n                Ok(()) =&gt; break,\n                Err(e) if attempt &lt; max_retries - 1 =&gt; {\n                    tracing::warn!(\n                        \"Retry {}\u002F{}: {:?}\",\n                        attempt + 1, max_retries, e\n                    );\n                    tokio::time::sleep(\n                        Duration::from_millis(100 * 2u64.pow(attempt))\n                    ).await;\n                }\n                Err(e) =&gt; {\n                    tracing::error!(\"Gagal setelah {} retry: {:?}\", max_retries, e);\n                    \u002F\u002F Kirim ke dead letter queue\n                    dead_letter_queue(&amp;event, &amp;e).await;\n                }\n            }\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"kesimpulan\">Kesimpulan\u003C\u002Fh2>\n\u003Cp>Arsitektur event-driven di Rust memberikan decoupling, extensibility, dan performa tinggi. tokio broadcast channel menyediakan primitif yang efisien untuk event bus, dan integrasi dengan Axum memungkinkan HTTP handler yang responsif tanpa memblokir pada side effect.\u003C\u002Fp>\n","id","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:25.129299Z","Membangun event bus di Rust dengan tokio broadcast, integrasi Axum, dan pola subscriber untuk sistem backend real-time.","event-driven Rust",null,"index, follow",[21,26,30],{"id":22,"name":23,"slug":24,"created_at":25},"c0000000-0000-0000-0000-000000000016","EVM","evm","2026-03-28T10:44:21.513630Z",{"id":27,"name":28,"slug":29,"created_at":25},"c0000000-0000-0000-0000-000000000022","Performance","performance",{"id":31,"name":32,"slug":33,"created_at":25},"c0000000-0000-0000-0000-000000000001","Rust","rust","Rekayasa",[36,43,50],{"id":37,"title":38,"slug":39,"excerpt":40,"locale":12,"category_name":41,"published_at":42},"d0000000-0000-0000-0000-000000000642","WASI 0.3 dan Kematian Cold Start: Wasm Sisi Server di Produksi","wasi-0-3-kematian-cold-start-wasm-sisi-server-di-produksi","WASI 0.3 dirilis pada Februari 2026 dengan async I\u002FO native, tipe stream, dan dukungan socket penuh. WebAssembly sisi server kini menghadirkan cold start dalam hitungan mikrodetik, dan setiap penyedia cloud besar menawarkan Wasm serverless.","DevOps","2026-03-28T10:44:47.445780Z",{"id":44,"title":45,"slug":46,"excerpt":47,"locale":12,"category_name":48,"published_at":49},"d0000000-0000-0000-0000-000000000620","Stack Backend Modern 2026: Rust + PostgreSQL 18 + Wasm + eBPF","stack-backend-modern-2026-rust-postgresql-wasm-ebpf","Empat teknologi konvergen untuk mendefinisikan ulang infrastruktur backend di 2026: Rust menghilangkan overhead garbage collection dan mengurangi jumlah container hingga 3x, PostgreSQL 18 menggantikan database khusus, WASI 0.3 memberikan cold start mikrodetik untuk fungsi serverless, dan eBPF memungkinkan observabilitas tanpa instrumentasi dengan biaya yang jauh lebih rendah dari monitoring tradisional.","Engineering","2026-03-28T10:44:45.804120Z",{"id":51,"title":52,"slug":53,"excerpt":54,"locale":12,"category_name":55,"published_at":56},"d0000000-0000-0000-0000-000000000571","Peta Jalan Skalabilitas Ethereum 2026: Glamsterdam, PeerDAS, dan 10.000 TPS","peta-jalan-skalabilitas-ethereum-2026-glamsterdam-peerdas-10000-tps","Ethereum menargetkan 10.000 transaksi per detik di seluruh ekosistem L1 dan L2 pada 2026. Dengan upgrade Glamsterdam dan Hegota di depan mata, ditambah PeerDAS untuk ketersediaan data, berikut peta jalan teknis lengkapnya.","Blockchain","2026-03-28T10:44:42.809066Z",{"id":13,"name":58,"slug":59,"bio":60,"photo_url":18,"linkedin":18,"role":61,"created_at":62,"updated_at":62},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]