Pattern CQRS (Command Query Responsibility Segregation) kết hợp Event Sourcing (Phần 1)

*Pattern CQRS (Command Query Responsibility Segregation) kết hợp Event Sourcing * _ENGLISH VERSION BELOW _ Thông thường, những architecture truyền thống sẽ tương tác CRUD với cơ sở dữ liệu bằng cách vừa đọc, vừa ghi. Tuy nhiên, khi ứng dụng phát triển lớn hơn, càng có nhiều request/response hơn có thể làm tăng “tải” cho hệ thống, việc thiết kế hệ thống theo hướng single data model sẽ có khả năng dẫn đến một số vấn đề : *1. Lock contention ( Tranh chấp khóa ) : * Trước khi chúng ta tìm hiểu về CQRS, mình sẽ nói về “cạnh tranh tài nguyên”. Ví dụ : Trường hợp một người dùng nhấp nút ĐẶT thì những API được gọi : API GET /flights/seats trả về số ghế trống hiện tại. API POST /flights/book để đặt chỗ. Khi người dùng nhấn ĐẶT thì findById() của phương thức GET còn phải đợi save() của phương thức POST cập nhật xong. Nếu hai người đặt cùng lúc một ghế (ví dụ: ghế A1), cả hai request có thể đọc cùng dữ liệu cũ và cả hai cùng đặt được ghế A1 . Do đó hệ thống phải locking dữ liệu để đảm bảo tính toàn vẹn. Việc locking có thể làm chậm hệ thống vì các request phải đợi nhau hoàn thành trước khi tiếp tục. *2. Data mismatch ( Sự khác biệt giữa đọc và ghi ) : * Data mismatch xảy ra khi dữ liệu được sử dụng cho thao tác đọc và ghi có cấu trúc không phù hợp với nhau, làm giảm hiệu suất. Trong thao tác ghi (write), khi người dùng tạo hoặc cập nhật một bài đăng, hệ thống cần phải ghi tất cả dữ liệu liên quan như title, content, tags, likes, authorInfo, và comments. Một số trường trong document này như authorInfo có thể là không cần thiết trong các thao tác đọc thông thường, nhưng chúng vẫn phải được ghi lại trong cơ sở dữ liệu. Trong thao tác đọc (read), khi người dùng chỉ muốn xem danh sách các bài đăng (chẳng hạn chỉ bao gồm tiêu đề và số lượng lượt thích), việc truy vấn tất cả các trường (bao gồm authorInfo, comments) có thể làm tăng tải dữ liệu không cần thiết. Từ đó, sử dụng các cách như tham chiếu dữ liệu, lưu trữ bộ nhớ đệm, sao chép vào các tài liệu khác nhau, gây khó khăn cho việc duy trì tính toàn vẹn dữ liệu. *3. Performance Problems ( Vấn đề hiệu suất) : * Giả sử hệ thống sử dụng kiến trúc truyền thống với một mô hình dữ liệu duy nhất cho cả các thao tác đọc và ghi: Trong một ứng dụng thương mại điện tử, khi hệ thống sử dụng mô hình dữ liệu duy nhất cho cả thao tác đọc và ghi, nó có thể gặp phải các vấn đề hiệu suất như tải quá mức lên cơ sở dữ liệu do phải xử lý đồng thời các yêu cầu đọc và ghi, truy vấn phức tạp làm tăng độ trễ khi lấy thông tin sản phẩm và bình luận, vấn đề khả năng mở rộng khi tải đọc và ghi tăng cao trong các thời điểm cao điểm, và thiếu tối ưu hóa cho các tác vụ đọc và ghi khiến cả hai loại thao tác bị ảnh hưởng lẫn nhau, giảm hiệu suất tổng thể của hệ thống. **4. Security challenges ( Khó khăn bảo mật) : **Việc quản lý bảo mật có thể gặp khó khăn khi các thực thể phải thực hiện cả thao tác đọc và ghi. Sự chồng chéo này có thể dẫn đến việc dữ liệu bị lộ ra trong những tình huống không mong muốn. Sử dụng CQRS có thể đơn giản hóa mọi thứ dù những vấn đề trên cũng vẫn sẽ có những cách giải quyết riêng. Pattern này giúp tách biệt rõ ràng giữa các thao tác đọc và ghi, từ đó cải thiện hiệu suất và dễ dàng xử lý các tình huống phức tạp. Bằng cách áp dụng các cải tiến như kiểm tra client-side, cải thiện logic máy chủ, và xử lý bất đồng bộ, bạn có thể tối ưu hóa cả hai mặt của hệ thống, giảm thiểu lỗi và cung cấp trải nghiệm người dùng tốt hơn. Ở bài sau mình sẽ nói về kiến trúc và cách sử dụng CQRS kết hợp Event Sourcing —----------------------------------- *CQRS Pattern (Command Query Responsibility Segregation) Combined with Event Sourcing * In traditional architectures, applications interact with the database using CRUD operations for both reading and writing. However, as applications scale and the number of requests increases, this approach can lead to performance bottlenecks. Designing the system with a single data model can result in several issues: *Lock Contention * Before diving into CQRS, let’s discuss "resource contention." Consider the following example: A user clicks the BOOK button, triggering two API calls: GET /flights/seats returns the number of available seats. POST /flights/book reserves a seat. If two users attempt to book the same seat (e.g., seat A1) simultaneously, both requests might retrieve outdated data and successfully reserve the same seat. To prevent this, the system must implement locking mechanisms to maintain data integrity. However, locking slows down the system because requests must wait for one another to complete before proceeding. Data Mismatch Data mismatch occurs when the structure of data used for reading and writing is not well aligned, leading to performance degradation. Write operations: When a user creates or updates a post, the system must store all related data, such as title, content, tags, likes, authorInfo, and comments. However, some fields, like authorInfo, may not be necessary for most read operations. Read operations: If a use

Apr 2, 2025 - 16:43
 0
Pattern CQRS (Command Query Responsibility Segregation) kết hợp Event Sourcing (Phần 1)

*Pattern CQRS (Command Query Responsibility Segregation) kết hợp Event Sourcing
*

_ENGLISH VERSION BELOW
_
Thông thường, những architecture truyền thống sẽ tương tác CRUD với cơ sở dữ liệu bằng cách vừa đọc, vừa ghi. Tuy nhiên, khi ứng dụng phát triển lớn hơn, càng có nhiều request/response hơn có thể làm tăng “tải” cho hệ thống, việc thiết kế hệ thống theo hướng single data model sẽ có khả năng dẫn đến một số vấn đề :

*1. Lock contention ( Tranh chấp khóa ) :
*

Trước khi chúng ta tìm hiểu về CQRS, mình sẽ nói về “cạnh tranh tài nguyên”. Ví dụ :
Trường hợp một người dùng nhấp nút ĐẶT thì những API được gọi :

API GET /flights/seats trả về số ghế trống hiện tại.
API POST /flights/book để đặt chỗ.

Khi người dùng nhấn ĐẶT thì findById() của phương thức GET còn phải đợi save() của phương thức POST cập nhật xong. Nếu hai người đặt cùng lúc một ghế (ví dụ: ghế A1), cả hai request có thể đọc cùng dữ liệu cũ và cả hai cùng đặt được ghế A1 . Do đó hệ thống phải locking dữ liệu để đảm bảo tính toàn vẹn. Việc locking có thể làm chậm hệ thống vì các request phải đợi nhau hoàn thành trước khi tiếp tục.

*2. Data mismatch ( Sự khác biệt giữa đọc và ghi ) :
*

Data mismatch xảy ra khi dữ liệu được sử dụng cho thao tác đọc và ghi có cấu trúc không phù hợp với nhau, làm giảm hiệu suất.

Trong thao tác ghi (write), khi người dùng tạo hoặc cập nhật một bài đăng, hệ thống cần phải ghi tất cả dữ liệu liên quan như title, content, tags, likes, authorInfo, và comments. Một số trường trong document này như authorInfo có thể là không cần thiết trong các thao tác đọc thông thường, nhưng chúng vẫn phải được ghi lại trong cơ sở dữ liệu.

Trong thao tác đọc (read), khi người dùng chỉ muốn xem danh sách các bài đăng (chẳng hạn chỉ bao gồm tiêu đề và số lượng lượt thích), việc truy vấn tất cả các trường (bao gồm authorInfo, comments) có thể làm tăng tải dữ liệu không cần thiết.

Từ đó, sử dụng các cách như tham chiếu dữ liệu, lưu trữ bộ nhớ đệm, sao chép vào các tài liệu khác nhau, gây khó khăn cho việc duy trì tính toàn vẹn dữ liệu.

*3. Performance Problems ( Vấn đề hiệu suất) :
*

Giả sử hệ thống sử dụng kiến trúc truyền thống với một mô hình dữ liệu duy nhất cho cả các thao tác đọc và ghi:
Trong một ứng dụng thương mại điện tử, khi hệ thống sử dụng mô hình dữ liệu duy nhất cho cả thao tác đọc và ghi, nó có thể gặp phải các vấn đề hiệu suất như tải quá mức lên cơ sở dữ liệu do phải xử lý đồng thời các yêu cầu đọc và ghi, truy vấn phức tạp làm tăng độ trễ khi lấy thông tin sản phẩm và bình luận, vấn đề khả năng mở rộng khi tải đọc và ghi tăng cao trong các thời điểm cao điểm, và thiếu tối ưu hóa cho các tác vụ đọc và ghi khiến cả hai loại thao tác bị ảnh hưởng lẫn nhau, giảm hiệu suất tổng thể của hệ thống.

**4. Security challenges ( Khó khăn bảo mật) :
**Việc quản lý bảo mật có thể gặp khó khăn khi các thực thể phải thực hiện cả thao tác đọc và ghi. Sự chồng chéo này có thể dẫn đến việc dữ liệu bị lộ ra trong những tình huống không mong muốn.

Sử dụng CQRS có thể đơn giản hóa mọi thứ dù những vấn đề trên cũng vẫn sẽ có những cách giải quyết riêng. Pattern này giúp tách biệt rõ ràng giữa các thao tác đọc và ghi, từ đó cải thiện hiệu suất và dễ dàng xử lý các tình huống phức tạp. Bằng cách áp dụng các cải tiến như kiểm tra client-side, cải thiện logic máy chủ, và xử lý bất đồng bộ, bạn có thể tối ưu hóa cả hai mặt của hệ thống, giảm thiểu lỗi và cung cấp trải nghiệm người dùng tốt hơn.

Ở bài sau mình sẽ nói về kiến trúc và cách sử dụng CQRS kết hợp Event Sourcing
—-----------------------------------
*CQRS Pattern (Command Query Responsibility Segregation) Combined with Event Sourcing
*

In traditional architectures, applications interact with the database using CRUD operations for both reading and writing. However, as applications scale and the number of requests increases, this approach can lead to performance bottlenecks. Designing the system with a single data model can result in several issues:

*Lock Contention
*

Before diving into CQRS, let’s discuss "resource contention." Consider the following example:
A user clicks the BOOK button, triggering two API calls:
GET /flights/seats returns the number of available seats.

POST /flights/book reserves a seat.

If two users attempt to book the same seat (e.g., seat A1) simultaneously, both requests might retrieve outdated data and successfully reserve the same seat. To prevent this, the system must implement locking mechanisms to maintain data integrity. However, locking slows down the system because requests must wait for one another to complete before proceeding.
Data Mismatch
Data mismatch occurs when the structure of data used for reading and writing is not well aligned, leading to performance degradation.
Write operations: When a user creates or updates a post, the system must store all related data, such as title, content, tags, likes, authorInfo, and comments. However, some fields, like authorInfo, may not be necessary for most read operations.

Read operations: If a user only wants to see a list of posts (e.g., displaying only the title and number of likes), querying unnecessary fields like authorInfo and comments increases the data load.

To address this, developers may use data references, caching, or document duplication, but these approaches complicate data integrity maintenance.
Performance Problems
A traditional architecture that relies on a single data model for both reading and writing can face several performance issues:
Database Overload: Handling simultaneous read and write requests can strain the database.

Query Complexity: Retrieving product details and comments in an e-commerce system may introduce high query latency.

Scalability Issues: During peak traffic, simultaneous read and write operations can cause bottlenecks.

Lack of Optimization: Read and write operations can interfere with each other, reducing overall system performance.

Security Challenges
Managing security becomes more complex when entities handle both read and write operations. Overlapping responsibilities may expose sensitive data in unintended scenarios.
How CQRS Helps

Although each of the issues above has individual solutions, CQRS simplifies the overall architecture by separating read and write operations. This separation enhances performance and makes it easier to handle complex scenarios. By implementing improvements such as client-side validation, optimized server logic, and asynchronous processing, developers can optimize both read and write operations, reduce errors, and provide a better user experience.

In the next article, I will discuss the architecture and implementation of CQRS combined with Event Sourcing.