Tổng quan về kiến trúc CQRS
CQRS ?
Đây là một pattern được viết tắt bởi Command Query Responsibility Segregation, dịch nôm na là phân tách vai trò Command (tượng trưng cho việc ghi dữ liệu) và Query (tượng trưng cho việc đọc dữ liệu). CQRS được mô tả lần đầu bởi tác giả Greg Young.
CQRS vs CQS
Tương tự với CQRS là pattern CQS (Command Query Separation) được giới thiệu bởi Bertrand Meyer trong cuốn Object Oriented Software Construction. CQRS được coi như là một mở rộng của CQS. Có thể coi CQS thể hiện ở mức class hoặc component level còn CQRS thể hiện ở bounded context level.
CQS yêu cầu tách biệt các method cho việc query hoặc writing cho 1 model: Query không được thay đổi state, trong khi Command được thay đổi state nhưng không trả về 1 giá trị nào.
CQRS cũng có ý tưởng tương tự nhưng rõ ràng hơn. Một Query request sẽ tách biệt với Command request. Một Query trả về data mà không thay đổi state của system, Command thay đổi system nhưng không trả về data. Sự khác biệt cơ bản là trong mô hình CQRS, tất cả các object sẽ được chia thành 2 object: 1 object cho Query và 1 object cho Command.
ON THE WAY TO CQRS
Để hiểu rõ hơn về kiến trúc CQRS, ta sẽ bắt đầu từ kiến trúc truyền thống (n-tier (layer) architecture). Trong kiến trúc N-Tier layer, việc thay đổi dữ liệu thường sử dụng CRUD (create, read, update và delete)
Nếu chúng ta muốn sửa kiến trúc này theo nguyên lý CQS thì chúng ta chỉ đơn giản là tách khối Business logic thành Commands và Queries:
Nếu bạn đang cần phân tách rõ 2 side Query và Command trên hệ thống với mã nguồn đã dựng sẵn thì bước này chắc hẳn là bước khó khăn nhất.
Command: thực thi command là cách thức duy nhất để thay đổi state của hệ thống. Command chịu trách nhiệm cho tất cả các thay đổi trong hệ thống. Nếu không có Command nào thực thi, state của hệ thống sẽ giữ nguyên. Thực thi Command không nên trả về bất kỳ giá trị gì. (Nên thực thi tương tự kiểu void function)
Query: là thực thi việc đọc dữ liệu. Nó đọc state của system và có thể filter, aggregate và chuyển đổi form data theo định dạng mong muốn. Query nên trả về kết quả.
Tiếp tục trở lại kiến trúc ở trên, chúng ta có thể chuyển đổi Model trở thành Domain Model. Với Model, bạn hiểu được tập hợp các data container trong khi Domain Model đóng gói thành các nghiệp vụ phức tạp. Với việc chuyển đổi này, chúng ta có thể dễ hiểu Command chịu trách nhiệm cho việc thay đổi state trong những nghiệp vụ phức tạp và nên đặt ở Domain Model.
Ngoài ra, chúng ta có thể thấy rõ rằng Domain Model phù hợp cho việc ghi dữ liệu trong khi nó không nhất thiết cần cho việc đọc dữ liệu.
Tiếp theo, chúng ta có thể ánh xạ sử dụng READ Model với ORM (Object Relational Mapping) và sử dụng nó để build các câu truy vấn. Sử dụng ORM sẽ hữu ích để đơn giản mô hình này.
Hiện tại, vấn đề là chúng ta là vẫn có READ và WRITE model tách biệt chỉ ở mức logic level và chúng vẫn dùng chung database. Điều đó có nghĩa là READ model đơn giản chỉ là mức virtualized như DB Views, tốt hơn có thể là Materialized Views. Giải pháp này là OK nếu hệ thống của chúng ta không ảnh hưởng nhiều bởi vấn đề performance.
Tiếp theo, chúng ta có thể tách biệt hoàn toàn Data Model. READ Model sẽ được update bởi các event khi WRITE Domain Model thực thi. Đến bước này, chúng ta có thể gọi thiết kế này là CQRS
CQRS != Event Sourcing
Cần phân biệt giữa CQRS và Event Sourcing. Event Sourcing là một khái niệm thường được sử dụng với CQRS và nó có thể được xác định là 1 phần của CQRS. Ý tưởng của Event Sourcing rất đơn giản: Domain sẽ tạo ra các events (đó là các thay đổi của hệ thống). Nếu chúng ta lấy tất cả các event từ lúc bắt đầu hệ thống và thực hiện lại tại trạng thái ban đầu thì chúng ta sẽ có trạng thái hiện tại. Ví dụ thực tiễn là khi chúng ta mở tài khoản tại ngân hàng, các giao dịch (transaction) là các event. Khi mở tài khoản ban đầu thì tài khoản là 0 sau khi chúng ta thực hiện toàn bộ các event (transaction) thì chúng ta có tài khoản hiện tại.
Trong khi Event Sourcing là phương pháp hiệu quả để lưu các trạng thái của hệ thống thì nó cũng không nhất thiết có trong mô hình CQRS. Trong mô hình CQRS, việc cài đặt lưu trữ Domain Model thực tế chỉ là 1 tuỳ chọn.
Lợi ích của sử dụng event sourcing:
– Kiểm tra được audit log
– Có khả năng tạo query để truy vấn history data
– Decoupled side-effects
– Hỗ trợ việc troubleshooting và debuging.
READ and WRITE model
WRITE model
Trong việc xây dựng mô hình CQRS, việc xây dựng WRITE Model luôn là phức tập nhất, có thể coi WRITE Model như là trái tim của hệ thống. Ở đó tạo ra business decision quan trọng. Nó xác định trách nhiệm chính của kiến trúc này là thể hiện state thực của hệ thống, state có thể sử dụng để tạo ra các quyết định quan trọng.
READ Model
Việc xử lý đọc dữ liệu sẽ yêu cầu chúng ta phải sử dụng các truy vấn. Thường thì các query đều tốn khá nhiều time (có thể do sử dụng truy vấn JOIN). Có nhiều cách để cải tiến tốc độ truy vấn (filter, hay tính toán trước dữ liệu cần thiết,…). Tuy nhiên, thực tế hiệu quả nhất là sử dụng cache. Việc sử dụng cache hiệu quả sẽ cần cân nhắc kỹ lưỡng.
“There are only two hard things in Computer Science: cache invalidation and naming things. — Phil Karlton”