What is CQRS?
CQRS is a pattern in software architecture that separates:
- Commands → operations that change state (Create, Update, Delete)
- Queries → operations that read state (Get, List, Search)
The main idea: read and write operations are handled separately, allowing each to be optimized independently.
Benefits:
- Scalability (reads/writes can scale separately)
- Clear separation of concerns
- Easier to handle complex domain logic
CQRS Example in Java
Suppose we have a simple application to manage products.
1. Domain Model
public class Product {
private String id;
private String name;
private double price;
public Product(String id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
// Getters and Setters
}
2. Command Side (Write Operations)
import java.util.HashMap;
import java.util.Map;
public class ProductCommandService {
private Map<String, Product> productStore = new HashMap<>();
public void createProduct(String id, String name, double price) {
Product product = new Product(id, name, price);
productStore.put(id, product);
System.out.println("Product created: " + name);
}
public void updateProductPrice(String id, double newPrice) {
Product product = productStore.get(id);
if (product != null) {
product.setPrice(newPrice);
System.out.println("Product price updated: " + product.getName());
} else {
System.out.println("Product not found!");
}
}
// For demo purposes, exposing the store
public Map<String, Product> getProductStore() {
return productStore;
}
}
3. Query Side (Read Operations)
import java.util.Map;
public class ProductQueryService {
private Map<String, Product> productStore;
public ProductQueryService(Map<String, Product> productStore) {
this.productStore = productStore;
}
public Product getProductById(String id) {
return productStore.get(id);
}
public void listAllProducts() {
productStore.values().forEach(p ->
System.out.println("Product: " + p.getName() + ", Price: " + p.getPrice())
);
}
}
4. Using the Services
public class CQRSExample {
public static void main(String[] args) {
ProductCommandService commandService = new ProductCommandService();
// Write operations
commandService.createProduct("1", "Laptop", 1200);
commandService.createProduct("2", "Phone", 800);
commandService.updateProductPrice("2", 750);
// Read operations
ProductQueryService queryService = new ProductQueryService(commandService.getProductStore());
queryService.listAllProducts();
Product product = queryService.getProductById("1");
System.out.println("Fetched product: " + product.getName() + ", Price: " + product.getPrice());
}
}
How it works
- CommandService handles all writes (create/update/delete).
- QueryService handles all reads.
- They can use the same store (in-memory map for simplicity), but in real apps, the read model can be optimized for queries (like using a separate database or denormalized view).