CQRS là một mô hình kiến trúc cho việc xử lý CRUD dữ liệu, viết tắt của từ Command Query Responsibility Segregation. Mô hình này tách biệt hai thành phần chính là Command và Query.
- Command: Các thao tác ghi dữ liệu - WRITE.
- Query: Các thao tác đọc dữ liệu - READ.
Tư tưởng của CQRS là tách biệt thao tác Query và Command để tránh ảnh hưởng phụ đến quá trình kinh doanh và tạo ra những luồng xử lý rõ ràng, chặt chẽ.
Được gói gọn như sau:
- Query: Là các thao tác select và trả về dữ liệu, nhưng không thay đổi trạng thái của hệ thống.
- Command: Là các thao tác thay đổi dữ liệu/thay đổi trạng thái hệ thống, nhưng không trả về dữ liệu.
Mục đích của bài viết này là trình bày cách áp dụng CQRS vào coding, không nhằm giải thích các lập luận.
CQRS với Spring Boot demo
Trong bài viết này, chúng ta sẽ sử dụng Spring Boot để tạo một demo nhỏ về CQRS.
Để tạo dự án Spring Boot, bạn có thể sử dụng https://start.spring.io/ hoặc trên IDE như Intelij.
Cấu trúc dự án thông thường sẽ bao gồm các lớp như sau:
-
Service layer:
- IBookQueryService
- IBookCommandService
-
Controller layer:
- BookQueryController
- BookCommandController
-
Representations:
- BookQueryDto
- BookCreateCommandDto
Service
Thao tác giữa 2 vai trò READ và WRITE được tách biệt trong IBookQueryService
và IBookCommandService
như sau:
public interface IBookQueryService {
List getAllBooks();
Book getBookInfo(String bookId);
}
public interface IBookCommandService {
void create(String name, Date publishDate);
}
Controller
BookQueryController
sẽ gồm các resource SELECT và sẽ Autowired IBookQueryService
ở Query Controller tương ứng:
@Controller
public class BookQueryController {
@Autowired
private IBookQueryService bookQueryService;
@GetMapping("/books")
@ResponseBody
public List allBooks() {
return bookQueryService.getAllBooks();
}
@GetMapping("/books/{bookId}")
@ResponseBody
public ResponseEntity bookInfo(@PathVariable("bookId") String bookId) {
Book bookInfo = bookQueryService.getBookInfo(bookId);
if (bookInfo == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(bookInfo);
}
}
@Controller
public class BookCommandController {
@Autowired
private IBookCommandService bookCommandService;
@PostMapping("/book/create")
@ResponseStatus(HttpStatus.OK)
public void createBook(@RequestBody BookCreateCommandDto dto) {
bookCommandService.create(dto.getName(), dto.getPublishDate());
}
}
Representations
Tiếp theo là các data transfer object, các lớp này sẽ được convert từ Entity.
Trong ví dụ này, Book
là một minh họa của class Entity mapping với table trong database.
@Data
public class BookCreateCommandDto {
private String name;
private Date publishDate;
}
Cơ bản, chúng ta đã trình bày xong tổng quát về CQRS.
Tư tưởng của CQRS rất rõ ràng và chặt chẽ. Một Query request cần được tách biệt với Command request.
- Query: Chỉ select và trả về dữ liệu, nhưng không thay đổi dữ liệu.
- Command: Thay đổi dữ liệu, nhưng không trả về dữ liệu.
Nếu bạn quan tâm, bạn có thể tải mã nguồn đầy đủ từ đường dẫn sau: Full source code demo