Apa itu CQRS (Command Query Responsibility Segregation)?
CQRS (Command Query Responsibility Segregation) adalah pola desain dan arsitektur pemrograman yang memisahkan proses pengambilan data (query) dan perubahan data (command). Pola ini menggunakan command handler untuk menyederhanakan proses query dan menyembunyikan kompleksitas perubahan data yang bisa melibatkan banyak sistem.
Saat digabungkan dengan pola event sourcing, CQRS bisa menjamin adanya log audit terhadap perubahan di database. Ini berguna untuk menjaga konsistensi transaksi. Selain itu, model pembacaannya (read model) bisa berisi, atau digunakan untuk membentuk, materialized view dari data model penulisan (write model).
Pola ini bekerja dengan cara memisahkan logika aplikasi secara vertikal: satu sisi untuk command, satu lagi untuk query. Jadi, kita bisa memisahkan operasi membaca dan memperbarui data di sistem. Tujuan utama CQRS adalah untuk meningkatkan performa, keamanan, dan skalabilitas aplikasi.
Dalam CQRS, command mewakili niat pengguna—instruksi untuk melakukan sesuatu di sistem. Command tidak menghasilkan output dan merupakan bagian dari write model. Sebaliknya, query adalah permintaan informasi oleh user, yang mengembalikan hasil tapi tidak mengubah data—bagian dari read model.
Greg Young mengembangkan pola desain CQRS sekitar tahun 2010, berbarengan dengan munculnya pola event sourcing. Menurutnya, “[CQRS] menggunakan definisi command dan query seperti yang dijelaskan Meyer, dan memisahkan objek menjadi dua: satu untuk command dan satu untuk query.” Yang dimaksud adalah Bertrand Meyer, pencetus pola sebelumnya yaitu Command Query Separation (CQS).
CQRS vs. CQS
Meski CQRS terinspirasi dari CQS, keduanya berbeda. Pada CQRS, objek dipecah menjadi dua: command dan query. Pemisahan ini bikin CQRS cocok banget untuk aplikasi berbasis event sourcing atau UI berbasis tugas (UI). Sementara itu, CQS lebih fokus pada pemisahan logika yang mengubah state dengan yang tidak.
Bertrand Meyer memperkenalkan CQS dalam bukunya Object-Oriented Software Construction dan merupakan bagian dari kontribusinya di bahasa pemrograman Eiffel. Ia menyarankan agar metode yang mengubah state dipisahkan dari yang tidak, agar query bisa digunakan secara aman dalam berbagai konteks.
Kapan Sebaiknya Menggunakan CQRS?
Pola CQRS cocok digunakan dalam situasi berikut:
- Kolaborasi antar pengguna. Banyak pengguna bisa mengakses data yang sama secara bersamaan.
- UI berbasis tugas. Pengguna dibimbing melalui serangkaian langkah untuk menyelesaikan tugas.
- Sistem dengan trafik tinggi. Beban kerja antara operasi baca/tulis bisa dibagi, meningkatkan performa dan skalabilitas.
- Query ke repositori. Data bisa ditarik dari repositori sesuai kebutuhan pengguna.
- Logika bisnis kompleks. Memisahkan proses baca/tulis mempermudah manajemen logika yang rumit.
- Optimasi proses baca. Bisa membuat read model khusus yang lebih efisien.
- Model data yang berbeda untuk baca/tulis. CQRS memungkinkan pemisahan model data untuk masing-masing proses.
- Dukungan untuk event sourcing. Bisa dikombinasikan dengan event sourcing untuk menangani banyak event dan query.
Meski powerful, CQRS tidak cocok untuk semua kasus. Jangan digunakan untuk sistem sederhana yang hanya butuh CRUD (create, read, update, delete).
Bagaimana CQRS Dieksekusi
Cara paling umum menerapkan CQRS adalah dengan menggunakan command pattern, yaitu sistem yang mendefinisikan interface tingkat tinggi. Saat runtime, kelas dasar (base class) mengambil command, membuat handler yang sesuai (seperti update, delete, atau create), lalu menjalankan perintah itu.
Sebelum dan sesudah eksekusi, base class bisa mencatat log pemanggilan. Log ini bisa di-*replay* kapan pun. Program yang menjalankan event dari log ini biasanya berupa for loop, membaca file baris per baris dan mengeksekusi command sesuai datanya. Kompleksitasnya disembunyikan atau dienkapsulasi oleh handler.
Handler akan menjalankan semua langkah untuk membuat, memperbarui, atau menghapus item dalam sistem. Handler ini bisa juga menangani kesalahan dan rollback bila diperlukan. Dalam kasus kompleks, handler menulis hasilnya ke dalam two-phase commit (atau saga), mencatat semua perubahan di event log. Jika log ini bisa dibaca dan di-*replay*, maka sistem mendukung event sourcing.
Contoh Penggunaan CQRS: Proses Pemesanan Pelanggan
Saat pelanggan melihat pesanan, sistem hanya melakukan proses baca dari database. Misalnya menggunakan NoSQL seperti Redis. Informasi dibaca oleh microservice, lalu ditampilkan oleh website.
Namun, sisi tulisannya jauh lebih kompleks. Misalnya jika pelanggan membatalkan pesanan sebelum dikirim:
- Pesanan harus dibatalkan di cache dan sumber data utama.
- Jika ada data warehouse, data juga harus dihapus dari sana.
- Gudang dan bagian pengiriman harus diberi tahu agar tidak mengirim barang.
- Transaksi kartu kredit harus dibatalkan, jumlah stok diperbarui, dan pesanan ke supplier dikurangi.
Karena satu perubahan berdampak ke banyak sistem, maka command handler seperti saga pattern atau ESB sangat dibutuhkan untuk mengatur proses ini. Di sinilah CQRS membantu.
Tantangan dalam Menggunakan CQRS
- Kompleksitas. CQRS menambah layer tambahan ke sistem seperti handler, dispatcher, dan logging.
- Messaging. Biasanya butuh sistem messaging untuk proses command dan broadcast update.
- Konversi sistem lama. Sulit diterapkan ke sistem sederhana yang tidak pakai event log.
- Konsistensi data. Karena read/write model terpisah, data bisa jadi tidak sinkron.
- Delay sinkronisasi. Perubahan di write model bisa butuh waktu sebelum muncul di read model.
- Biaya. Implementasi CQRS bisa butuh lebih banyak resource dan infrastruktur.
Menggabungkan CQRS dengan Pola Lain: Event Sourcing
CQRS sering digabung dengan event sourcing. Setiap perubahan pada data disimpan sebagai event. Sistem bisa direkonstruksi dari rangkaian event tersebut, membentuk log transaksi yang bisa di-*replay*—bahkan kadang bisa di-*rollback*.
Untuk mendukung event sourcing, command handler akan menulis setiap perubahan ke log. Setiap event bisa dijalankan ulang, menjadikan log tersebut sebagai catatan hidup dari seluruh histori data.
Keduanya saling melengkapi: CQRS memisahkan tanggung jawab, dan event sourcing menyimpan semua perubahan yang terjadi dari waktu ke waktu.
Pola-Pola Terkait
CQRS cocok digabungkan dengan domain-driven design, event sourcing, dan command pattern. Dalam arsitektur yang lebih luas, pola ini biasanya berada dalam konteks service-oriented architecture (SOA).