Memahami CQRS (Command Query Responsibility Segregation) Kenapa dan Bagaimana Menggunakannya
Tags: programming, go
Published: 2024-06-04 03:55:05


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sy97ghzlqv5h4dwzizsb.png)

### Apa Itu CQRS?

CQRS atau _Command Query Responsibility Segregation_ adalah pola desain arsitektur yang memisahkan operasi data menjadi dua kategori: **command** dan **query**. Ini berarti operasi untuk mengubah data (command) dipisahkan dari operasi untuk membaca data (query). Tujuannya adalah meningkatkan performa, skalabilitas, dan maintainability aplikasi.

### Kenapa Perlu CQRS?

**1. Performance Lebih Nendang**
Dengan memisahkan command dan query, kita bisa mengoptimalkan masing-masing proses secara terpisah. Misalnya, untuk query, kita bisa menggunakan database yang dioptimasi untuk kecepatan baca (_read_). Sebaliknya, untuk command, kita bisa fokus pada konsistensi data.

**2. Scalability Lebih Mudah**
Dalam aplikasi besar, kebutuhan untuk membaca data seringkali jauh lebih tinggi dibandingkan dengan mengubah data. Dengan CQRS, kita bisa menskalakan bagian query dan command secara independen. Jika tiba-tiba ada lonjakan permintaan untuk membaca data, kita bisa menambah instance query tanpa mengubah bagian command.

**3. Keamanan dan Konsistensi**
Dengan memisahkan command dan query, kita bisa lebih mudah mengatur akses. Misalnya, hanya bagian command yang bisa menulis (_create, update, delete_) ke database, sementara bagian query hanya bisa membaca (_read_). Ini membantu menjaga konsistensi dan integritas data.

### Cara Kerja CQRS
Misalnya, kita punya aplikasi e-commerce. Operasi utamanya bisa dibagi menjadi:

- Command: Menambah produk, mengubah harga, menghapus produk.
- Query: Menampilkan daftar produk, menampilkan detail produk.

Dengan CQRS, kita akan membuat dua model yang terpisah untuk menangani operasi ini.

### Implementasi CQRS dengan Go

Mari kita lihat bagaimana kita bisa mengimplementasikan CQRS dalam bahasa pemrograman Go.

**Command Side**
Pertama, kita akan membuat bagian untuk menangani command, yaitu operasi yang mengubah data.

```go
package main

import (
	"fmt"
)

type Command interface {
	Execute() error
}

type AddProductCommand struct {
	ProductName string
	Price       float64
}

func (c *AddProductCommand) Execute() error {
	// Logika buat nambah produk ke database
	fmt.Printf("Menambah produk: %s dengan harga: %.2f\n", c.ProductName, c.Price)
	// Misalnya kita simpan ke database di sini
	return nil
}

type UpdateProductCommand struct {
	ProductName string
	NewPrice    float64
}

func (c *UpdateProductCommand) Execute() error {
	// Logika buat update produk di database
	fmt.Printf("Mengupdate produk: %s menjadi harga: %.2f\n", c.ProductName, c.NewPrice)
	// Misalnya kita update database di sini
	return nil
}

func main() {
	addCmd := &AddProductCommand{ProductName: "Kaos Keren", Price: 150000}
	addCmd.Execute()

	updateCmd := &UpdateProductCommand{ProductName: "Kaos Keren", NewPrice: 175000}
	updateCmd.Execute()
}

```

Di sini, kita punya dua command: `AddProductCommand` untuk menambah produk baru dan `UpdateProductCommand` untuk memperbarui harga produk yang sudah ada.

**Query Side**
Sekarang, kita buat bagian untuk menangani query, yaitu operasi yang mengambil data.

```go
package main

import (
	"fmt"
)

type Product struct {
	Name  string
	Price float64
}

type Query interface {
	Execute() ([]Product, error)
}

type GetProductsQuery struct{}

func (q *GetProductsQuery) Execute() ([]Product, error) {
	// Logika buat fetch data produk dari database
	// Misalnya ini data dari database
	products := []Product{
		{Name: "Kaos Keren", Price: 150000},
		{Name: "Celana Jeans", Price: 200000},
	}

	return products, nil
}

func main() {
	getProductsQuery := &GetProductsQuery{}
	products, _ := getProductsQuery.Execute()

	for _, product := range products {
		fmt.Printf("Produk: %s, Harga: %.2f\n", product.Name, product.Price)
	}
}

```

Di sini, kita punya query `GetProductsQuery` yang mengambil data produk dari database (ini masih simulasi, bro).

### Studi Kasus: Toko Online

Bayangkan temen-temen punya toko online yang super rame. User pada ngecek produk terus, tapi ada juga yang sering nambahin atau update produk. Dengan CQRS, kita bisa handle skenario ini dengan efisien:

**1. Scalability**
Jika tiba-tiba ada lonjakan permintaan untuk membaca data (misalnya saat ada promo besar-besaran), kita bisa menambah instance query service tanpa harus mengubah bagian command. Kita juga bisa menggunakan _read-replica_ database untuk bagian query, sehingga bisa menangani lebih banyak permintaan baca.

**2. Performance**
Bagian query bisa dioptimasi untuk kecepatan. Misalnya, kita bisa menggunakan teknik _caching_ untuk menyimpan hasil query yang sering diakses. Dengan begitu, user mendapatkan respons yang lebih cepat. Di sisi lain, bagian command bisa fokus pada validasi data tanpa terganggu oleh query yang berat.

**3. Maintainability**
Dengan memisahkan command dan query, tim pengembang bisa bekerja secara paralel tanpa saling mengganggu. Misalnya, tim yang bekerja pada fitur baru untuk menambah produk (command) tidak perlu khawatir tentang performa query yang sedang dioptimasi oleh tim lain.

### Kesimpulan
CQRS adalah pola desain yang powerful untuk aplikasi skala besar. Dengan memisahkan command dan query, kita bisa mencapai performa yang lebih baik, skalabilitas yang lebih mudah, dan maintainability yang lebih baik. Implementasi CQRS dalam Go cukup sederhana, tetapi dampaknya pada performa dan skalabilitas aplikasi sangat signifikan.

Semoga penjelasan ini membantu memahami CQRS lebih dalam dan bagaimana mengimplementasikannya dalam aplikasi nyata. Selamat ngoding dan jangan lupa ngoding itu diketik jangan dipikir! Sampai jumpa di artikel yang lain ya broo!