# Arsitektur Software: MVC, CQRS, Event-Driven

> Bandingkan MVC, CQRS, dan Event-Driven Architecture. Pelajari kapan setiap pola cocok untuk use case Anda, trade-off, dan cara menggabungkannya dalam aplikasi modern.

URL: https://www.matthewswong.com/id/blog/software-architecture-patterns/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2025-02-19
- Updated: 2025-02-19
- Category: Software Development

Pola arsitektur software adalah solusi yang dapat digunakan kembali untuk masalah struktural yang berulang dalam desain software. Pola yang tepat dapat membuat aplikasi Anda lebih mudah diskalakan, ditest, dan dikembangkan. Pola yang salah dapat menciptakan kompleksitas yang tidak perlu yang memperlambat tim Anda selama bertahun-tahun. Postingan ini membandingkan tiga pola terpenting — MVC, CQRS, dan Event-Driven Architecture — dengan contoh TypeScript konkret untuk membantu Anda memilih dan menggabungkannya dengan percaya diri.

## MVC: Fondasi

Model-View-Controller adalah pola arsitektur yang paling banyak digunakan dalam pengembangan web. Ini memisahkan aplikasi menjadi tiga komponen yang saling terhubung: Model (data dan logika bisnis), View (lapisan presentasi), dan Controller (penanganan request dan koordinasi). Keawetan MVC berasal dari kesederhanaan dan keakrabannya yang luas — hampir setiap web framework mendukungnya, dan sebagian besar developer memahaminya segera.

### MVC dalam Konteks Next.js App Router

Next.js modern memetakan secara alami ke konsep MVC bahkan tanpa terminologi MVC yang eksplisit. React Server Component bertindak sebagai View, Route Handler bertindak sebagai Controller, dan lapisan data Anda (model Prisma, kelas service, fungsi repository) bertindak sebagai Model. Co-location UI dan pengambilan data di komponen halaman yang sama di App Router sedikit mengaburkan batas, tetapi pemisahan kepentingan tetap ada.

> Di Next.js App Router, pilih React Server Component untuk pengambilan data daripada API route handler. RSC menghilangkan satu network round-trip dan mengurangi JavaScript di sisi client. Gunakan Route Handler hanya untuk request yang diinisiasi client (pengiriman form, mutasi dari komponen 'use client').

## CQRS: Memisahkan Read dari Write

Command Query Responsibility Segregation memisahkan bagian sistem Anda yang mengubah state (Command) dari bagian yang membaca state (Query). Pemisahan ini memungkinkan Anda mengoptimalkan masing-masing secara independen — read model yang dioptimalkan untuk tampilan UI tertentu dapat memiliki bentuk yang sama sekali berbeda dari write model yang menerapkan aturan bisnis. CQRS powerful tetapi menambah kompleksitas; paling dibenarkan dalam sistem di mana read dan write memiliki kebutuhan scaling yang sangat berbeda.

### Command dan Query dalam TypeScript

Command mewakili maksud untuk mengubah state: PlaceOrderCommand, CancelSubscriptionCommand. Ia harus divalidasi dan ditangani oleh handler khusus. Query meminta proyeksi state yang dioptimalkan untuk use case tertentu: GetOrderSummaryQuery mengembalikan DTO datar yang dirancang untuk UI daftar pesanan. Read model dapat berupa SQL view, Redis cache, atau database yang dioptimalkan untuk read — CQRS tidak mengatur implementasinya.

### Event Sourcing Dengan CQRS

CQRS sering dipasangkan dengan Event Sourcing, di mana write model menyimpan urutan event (OrderPlaced, ItemShipped, OrderCancelled) daripada state saat ini. Read model dibangun dengan memutar ulang event ini. Ini memberi Anda jejak audit lengkap, kemampuan untuk memutar ulang history guna membangun read model baru, dan integrasi alami dengan Event-Driven Architecture. Trade-off-nya adalah kompleksitas yang jauh lebih tinggi — jangan mengadopsi Event Sourcing tanpa kebutuhan yang meyakinkan.

## Event-Driven Architecture

Event-Driven Architecture (EDA) adalah pola di mana komponen berkomunikasi dengan mengeluarkan dan mengonsumsi event secara asinkron melalui message broker (Kafka, RabbitMQ, AWS EventBridge). EDA memisahkan producer dari consumer — order service menerbitkan event 'order.placed' tanpa mengetahui atau peduli layanan mana yang mengonsumsinya. Ini memungkinkan coupling yang sangat longgar, tetapi memerlukan pemikiran matang tentang evolusi schema event, jaminan urutan, dan penanganan error.

### Event, Command, dan Query: Perbedaannya

Event adalah fakta yang terjadi di masa lalu ('pesanan telah ditempatkan'). Command adalah permintaan agar sesuatu terjadi ('tempatkan pesanan ini'). Query meminta state saat ini. Event disiarkan — sejumlah consumer dapat bereaksi. Command diarahkan — satu handler bertanggung jawab. Memahami perbedaan ini mencegah kesalahan desain umum seperti menamai event sebagai command.

> **Event-Driven Bukan Peluru Perak**

> EDA memperkenalkan eventual consistency — ketika order service menerbitkan event dan inventory service mengonsumsinya, ada jendela di mana inventaris belum diperbarui. Ini dapat diterima untuk banyak use case tetapi berbahaya untuk yang lain (misalnya, menagih pelanggan sebelum mengonfirmasi inventaris). Selalu analisis kebutuhan konsistensi Anda sebelum mengadopsi EDA, dan implementasikan compensating transaction (saga) untuk alur yang memerlukan konsistensi kuat.

### Pola Saga untuk Distributed Transaction

Ketika operasi mencakup beberapa layanan — tempatkan pesanan, reservasi inventaris, tagih pembayaran — Anda tidak dapat menggunakan database transaction lintas batas layanan. Pola Saga mengorkestrasikan serangkaian transaksi lokal, setiap langkah menerbitkan event yang memicu langkah berikutnya. Jika ada langkah yang gagal, compensating transaction akan membatalkan langkah sebelumnya. Choreography saga terdesentralisasi; orchestration saga menggunakan koordinator terpusat.

## Menggabungkan Pola dalam Praktik

Aplikasi nyata jarang menggunakan satu pola secara terisolasi. Kombinasi umum: MVC untuk lapisan request/response, CQRS untuk memisahkan jalur read dan write untuk domain yang kompleks, dan EDA untuk mengintegrasikan lintas batas layanan dan menangani async workflow. Kuncinya adalah memahami masalah apa yang dipecahkan setiap pola dan menerapkannya secara bedah pada bagian sistem Anda di mana trade-off masuk akal.

### Kerangka Keputusan Arsitektur Praktis

Mulai dengan MVC. Tambahkan CQRS ketika read dan write model Anda berbeda secara signifikan atau ketika performa read menjadi bottleneck. Tambahkan EDA ketika Anda perlu memisahkan layanan, menangani async workflow (notifikasi email, background processing), atau mengintegrasikan dengan sistem eksternal. Tambahkan Event Sourcing hanya ketika Anda benar-benar membutuhkan jejak audit atau kemampuan memutar ulang history.

### Dokumentasi Arsitektur: Architecture Decision Records

Dokumentasikan keputusan arsitektur signifikan menggunakan Architecture Decision Records (ADR). ADR menangkap konteks (mengapa kita membuat keputusan ini?), opsi yang dipertimbangkan, keputusan yang dibuat, dan konsekuensinya (positif dan negatif). ADR tinggal di repository Anda bersama kode dan memberikan catatan historis yang sangat berharga tentang mengapa sistem Anda seperti itu — konteks penting untuk onboarding engineer baru.

> Gunakan Structurizr DSL atau diagram C4 model untuk mendokumentasikan arsitektur Anda di beberapa level abstraksi (sistem, container, komponen). Diagram otomatis sebagai kode mencegah documentation drift dan memberi anggota tim baru peta sistem yang dapat dinavigasi.

## Memilih Pola yang Tepat

Setiap pola arsitektur ada untuk menyelesaikan masalah tertentu. Cocokkan pola dengan masalah, bukan sebaliknya. MVC menyelesaikan pemisahan presentasi, data, dan logika kontrol. CQRS menyelesaikan ketidakcocokan impedansi antara read dan write yang dioptimalkan. EDA menyelesaikan coupling antara komponen yang perlu bereaksi terhadap perubahan state satu sama lain. Jika ragu, mulai lebih sederhana dan berkembang — kompleksitas arsitektur jauh lebih mudah ditambahkan daripada dihapus.

## Source links

- https://martinfowler.com/bliki/CQRS.html
- https://martinfowler.com/eaaDev/EventSourcing.html
- https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs
- https://www.oreilly.com/library/view/building-microservices/9781491950340/

## FAQ

### Kapan sebaiknya saya memilih CQRS daripada MVC biasa?

Tambahkan CQRS ketika read model dan write model Anda berbeda secara signifikan atau ketika performa read menjadi bottleneck. Dalam MVC biasa, model yang sama melayani pembacaan sekaligus penulisan, yang memaksa trade-off ketika keduanya memiliki kebutuhan scaling yang sangat berbeda. CQRS memungkinkan Anda mengoptimalkan masing-masing sisi secara independen — misalnya, proyeksi read yang di-cache di Redis di samping write model yang ketat — namun menambah kompleksitas, jadi mulailah dengan MVC dan perkenalkan CQRS hanya saat perbedaannya sudah nyata.

### Apa perbedaan utama antara Event, Command, dan Query dalam Event-Driven Architecture?

Event adalah fakta yang sudah terjadi (misalnya, 'pesanan telah ditempatkan'), Command adalah permintaan agar sesuatu terjadi ('tempatkan pesanan ini'), dan Query meminta state saat ini. Event disiarkan — sejumlah consumer dapat bereaksi terhadapnya — sementara Command diarahkan ke satu handler. Mencampuradukkan konsep-konsep ini menimbulkan kesalahan desain umum seperti menamai event sebagai command atau memperlakukan query sebagai event.

### Trade-off konsistensi apa yang diperkenalkan Event-Driven Architecture, dan bagaimana cara mengatasinya?

EDA memperkenalkan eventual consistency: ketika order service menerbitkan event dan inventory service mengonsumsinya, ada jendela waktu di mana inventaris belum diperbarui. Ini dapat diterima untuk banyak use case, tetapi berbahaya untuk yang lain, seperti menagih pelanggan sebelum mengonfirmasi ketersediaan inventaris. Untuk alur yang memerlukan konsistensi kuat, implementasikan pola Saga — serangkaian transaksi lokal dengan compensating transaction yang membatalkan langkah sebelumnya jika ada langkah yang gagal.

### Bagaimana Next.js App Router memetakan ke konsep MVC?

Di Next.js App Router, React Server Component bertindak sebagai View, Route Handler bertindak sebagai Controller, dan lapisan data Anda — model Prisma, kelas service, atau fungsi repository — bertindak sebagai Model. Rekomendasi dalam postingan ini adalah memilih React Server Component untuk pengambilan data daripada API Route Handler karena RSC menghilangkan satu network round-trip dan mengurangi JavaScript di sisi client. Route Handler sebaiknya hanya digunakan untuk request yang diinisiasi client, seperti pengiriman form atau mutasi dari komponen 'use client'.

### Haruskah saya mengadopsi Event Sourcing bersamaan dengan CQRS?

Tidak harus. CQRS dan Event Sourcing sering dipasangkan, tetapi Event Sourcing membawa kompleksitas yang jauh lebih tinggi — ia menyimpan urutan event (OrderPlaced, ItemShipped, OrderCancelled) alih-alih state saat ini, dan read model dibangun ulang dengan memutar ulang event tersebut. Anda sebaiknya mengadopsi Event Sourcing hanya ketika benar-benar membutuhkan jejak audit lengkap atau kemampuan memutar ulang history untuk membangun read model baru. Kerangka keputusan dalam postingan ini menempatkannya paling akhir: mulai dengan MVC, tambahkan CQRS jika diperlukan, tambahkan EDA jika diperlukan, dan tambahkan Event Sourcing hanya bila ada kebutuhan yang meyakinkan.

---

- HTML version: https://www.matthewswong.com/id/blog/software-architecture-patterns/
- English: https://www.matthewswong.com/en/blog/software-architecture-patterns/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
