Báo cáo đồ án: Phương pháp mô hình hóa hệ thống với CQRS (System Modeling)

Báo cáo đồ án phương pháp mô hình hóa: Tìm hiểu System Modeling cho CQRS. Phân tích, thiết kế hệ thống theo CQRS với mô hình hóa chi tiết.

Chuyên ngành

Công nghệ phần mềm

Người đăng

Ẩn danh

Thể loại

Báo cáo đồ án

2024

45
1
0

Phí lưu trữ

30 Point

Tóm tắt

I. Tổng quan phương pháp mô hình hóa CQRS cho hệ thống lớn

Phương pháp mô hình hóa hệ thống cho CQRS (Command and Query Responsibility Segregation) là một mẫu kiến trúc phần mềm tiên tiến, được giới thiệu bởi Greg Young. Nguyên tắc cốt lõi của CQRS là phân tách hoàn toàn trách nhiệm giữa các thao tác làm thay đổi trạng thái hệ thống (Commands - Lệnh) và các thao tác truy vấn dữ liệu (Queries - Truy vấn). Thay vì sử dụng một mô hình dữ liệu duy nhất cho cả đọc và ghi như trong kiến trúc CRUD truyền thống, CQRS đề xuất sử dụng hai mô hình riêng biệt. Một write model được thiết kế để xử lý các logic nghiệp vụ phức tạp và đảm bảo tính toàn vẹn dữ liệu khi ghi. Một hoặc nhiều read model được tối ưu hóa đặc biệt cho các nhu cầu truy vấn, giúp tăng tốc độ phản hồi và giảm độ phức tạp khi lấy dữ liệu. Nền tảng của CQRS bắt nguồn từ nguyên tắc CQS (Command Query Separation) do Bertrand Meyer định nghĩa, trong đó một phương thức hoặc là một lệnh thực hiện hành động, hoặc là một truy vấn trả về dữ liệu, nhưng không bao giờ là cả hai. CQRS mở rộng nguyên tắc này lên cấp độ kiến trúc, tạo ra sự tách biệt vật lý hoặc logic giữa hai luồng xử lý, từ đó mang lại khả năng mở rộng vượt trội, hiệu năng cao và mã nguồn dễ bảo trì hơn cho các hệ thống phức tạp.

1.1. Định nghĩa CQRS và nguyên tắc Command Query Separation CQS

CQRS, viết tắt của Command and Query Responsibility Segregation, là một mẫu kiến trúc phân chia rõ ràng các hoạt động ghi dữ liệu (Commands) và đọc dữ liệu (Queries). Theo định nghĩa của Greg Young, các đối tượng trong hệ thống được chia thành hai loại: một loại chứa các Commands để thay đổi trạng thái và một loại chứa các Queries để truy xuất dữ liệu. Nền tảng của nó là CQS (Command Query Separation), một nguyên tắc do Bertrand Meyer đề xuất. CQS phát biểu rằng một phương thức chỉ nên thực hiện một trong hai nhiệm vụ: hoặc là thay đổi trạng thái của đối tượng (command), hoặc là trả về dữ liệu (query), nhưng không bao giờ thực hiện cả hai cùng lúc. CQRS nâng tầm CQS từ cấp độ phương thức lên cấp độ đối tượng và kiến trúc tổng thể, tạo ra sự độc lập giữa mô hình ghi và mô hình đọc.

1.2. Phân biệt Command và Query trong kiến trúc phần mềm hiện đại

Trong mô hình hóa CQRS, việc phân biệt rạch ròi giữa CommandQuery là cực kỳ quan trọng. Một Command là bất kỳ thao tác nào có mục đích thay đổi trạng thái của hệ thống, ví dụ như tạo đơn hàng, cập nhật thông tin người dùng. Commands thường không trả về dữ liệu, chỉ báo cáo thành công hay thất bại và có thể được xử lý bất đồng bộ theo nguyên tắc “fire & forget”. Ngược lại, một Query là một thao tác chỉ đọc, không bao giờ được phép thay đổi trạng thái dữ liệu. Nhiệm vụ của nó là truy xuất và trả về dữ liệu để hiển thị cho người dùng, ví dụ như lấy danh sách sản phẩm hay xem chi tiết đơn hàng. Sự tách biệt này cho phép tối ưu hóa riêng biệt cho từng loại thao tác, cải thiện đáng kể hiệu năng và độ tin cậy của hệ thống.

II. Thách thức của mô hình CRUD và lý do cần đến CQRS

Mô hình CRUD (Create, Read, Update, Delete) là một phương pháp tiếp cận phổ biến và hiệu quả cho các ứng dụng đơn giản. Tuy nhiên, khi hệ thống phát triển với quy mô lớn và logic nghiệp vụ phức tạp, mô hình này bắt đầu bộc lộ nhiều hạn chế nghiêm trọng. Việc sử dụng một mô hình dữ liệu (data model) duy nhất cho cả thao tác đọc và ghi dẫn đến sự xung đột về yêu cầu. Các thao tác ghi (Create, Update, Delete) đòi hỏi tính nhất quán, các quy tắc nghiệp vụ chặt chẽ và xác thực dữ liệu nghiêm ngặt. Trong khi đó, các thao tác đọc (Read) lại yêu cầu sự linh hoạt, tốc độ nhanh và thường cần các cấu trúc dữ liệu được tối ưu hóa (denormalized) để tránh các phép nối (JOIN) tốn kém. Sự mâu thuẫn này khiến cho model trở nên cồng kềnh, khó bảo trì và khó mở rộng. Hơn nữa, việc tối ưu hóa cho một phía thường làm ảnh hưởng đến hiệu năng của phía còn lại. Ví dụ, việc chuẩn hóa dữ liệu để đảm bảo tính toàn vẹn khi ghi sẽ làm chậm các truy vấn đọc phức tạp. Đây chính là những vấn đề mà phương pháp mô hình hóa CQRS ra đời để giải quyết, bằng cách cung cấp một giải pháp kiến trúc cho phép tối ưu độc lập hai luồng đọc và ghi.

2.1. Hạn chế về khả năng mở rộng và hiệu năng của mô hình CRUD

Trong một hệ thống lớn, lưu lượng truy cập cho việc đọc và ghi thường không cân bằng. Thông thường, số lượng yêu cầu đọc dữ liệu lớn hơn rất nhiều so với yêu cầu ghi. Với mô hình CRUD, do dùng chung một mô hình và cơ sở dữ liệu, việc mở rộng (scale) phải được thực hiện đồng bộ cho cả hai. Điều này dẫn đến lãng phí tài nguyên, bởi vì việc mở rộng phần ghi vốn phức tạp và tốn kém hơn lại phải đi kèm với nhu cầu mở rộng của phần đọc. Hơn nữa, việc đọc và ghi trên cùng một tập dữ liệu có thể gây ra tranh chấp tài nguyên (resource contention) và khóa bảng (table locking), làm giảm đáng kể hiệu năng chung của hệ thống, đặc biệt là trong các hệ thống có lưu lượng truy cập cao.

2.2. Sự phức tạp hóa logic nghiệp vụ trong một mô hình duy nhất

Khi một ứng dụng phát triển, các quy tắc nghiệp vụ (business logic) ngày càng trở nên phức tạp. Trong kiến trúc CRUD, tất cả logic này, bao gồm xác thực dữ liệu, tính toán, và các quy trình phức tạp, đều bị nhồi nhét vào một mô hình duy nhất. Điều này làm cho đối tượng (model) trở nên cồng kềnh, khó hiểu và khó bảo trì. Một thay đổi nhỏ ở logic ghi có thể vô tình ảnh hưởng đến các chức năng đọc và ngược lại. Sự phụ thuộc lẫn nhau này làm tăng nguy cơ phát sinh lỗi và làm chậm quá trình phát triển các tính năng mới. CQRS giải quyết vấn đề này bằng cách tách biệt logic nghiệp vụ vào write model, giữ cho read model đơn giản và chỉ tập trung vào việc trình bày dữ liệu.

III. Hướng dẫn triển khai mô hình CQRS cơ bản và nâng cao

Triển khai phương pháp mô hình hóa CQRS không nhất thiết phải phức tạp và có thể được áp dụng theo nhiều cấp độ khác nhau tùy thuộc vào yêu cầu của dự án. Ở cấp độ cơ bản nhất, được gọi là Partial CQRS, sự tách biệt chỉ diễn ra ở tầng ứng dụng (application layer). Lập trình viên sẽ tạo ra các đối tượng và các lớp xử lý (handler) riêng biệt cho CommandQuery, nhưng chúng vẫn có thể cùng truy cập vào một cơ sở dữ liệu duy nhất. Cách tiếp cận này giúp mã nguồn trở nên rõ ràng, dễ quản lý hơn mà không làm tăng quá nhiều độ phức tạp về mặt hạ tầng. Ở cấp độ nâng cao hơn, hay Fully CQRS, sự tách biệt được thực hiện một cách triệt để. Hệ thống sẽ sử dụng hai cơ sở dữ liệu vật lý riêng biệt: một database được tối ưu cho việc ghi (ví dụ: một cơ sở dữ liệu quan hệ như MySQL với cấu trúc chuẩn hóa cao) và một database khác được tối ưu cho việc đọc (ví dụ: một cơ sở dữ liệu NoSQL như Elasticsearch hoặc một bản sao chỉ đọc được phi chuẩn hóa). Dữ liệu giữa hai database này thường được đồng bộ hóa thông qua một cơ chế bất đồng bộ, chẳng hạn như sử dụng Message Queue. Cách tiếp cận này mang lại hiệu năngkhả năng mở rộng tối đa.

3.1. Các bước triển khai Partial CQRS với một database chung

Để bắt đầu với CQRS, có thể áp dụng mô hình Partial CQRS. Bước đầu tiên là xác định và tạo ra các đối tượng Command (ví dụ: CreateOrderCommand) và Query (ví dụ: GetOrderDetailsQuery) riêng biệt. Tiếp theo, xây dựng các trình xử lý (Handlers) tương ứng cho từng command và query. CommandHandler sẽ chứa logic nghiệp vụ để thay đổi dữ liệu, trong khi QueryHandler sẽ thực hiện truy vấn để lấy dữ liệu. Mặc dù cả hai handler này đều tương tác với cùng một database, chúng sử dụng các mô hình (models) hoặc các phương thức truy cập dữ liệu khác nhau, giúp tách biệt rõ ràng về mặt logic. Cách này giúp cải thiện cấu trúc mã nguồn mà không yêu cầu thay đổi lớn về cơ sở hạ tầng.

3.2. Triển khai Fully CQRS Tách biệt database cho đọc và ghi

Mô hình Fully CQRS đòi hỏi việc tách biệt hoàn toàn cơ sở dữ liệu. Write model sẽ tương tác với một cơ sở dữ liệu chính (Write DB), thường là hệ quản trị cơ sở dữ liệu quan hệ (RDBMS) để đảm bảo tính toàn vẹn dữ liệu. Sau khi một Command được thực thi thành công và dữ liệu được ghi vào Write DB, một sự kiện (event) sẽ được phát sinh. Sự kiện này được đẩy vào một Message Queue (ví dụ: RabbitMQ, Kafka). Một tiến trình lắng nghe (subscriber) sẽ nhận sự kiện này và cập nhật dữ liệu vào một hoặc nhiều cơ sở dữ liệu đọc (Read DB). Read DB có thể là bất cứ loại cơ sở dữ liệu nào được tối ưu cho việc truy vấn nhanh, chẳng hạn như Elasticsearch cho tìm kiếm hoặc Redis cho caching. Cách tiếp cận này cho phép khả năng mở rộng độc lập và tối ưu hóa hiệu năng cho từng hệ thống.

IV. Sức mạnh kết hợp CQRS Event Sourcing ES và DDD

Mặc dù CQRS có thể được sử dụng độc lập, sức mạnh thực sự của nó được phát huy tối đa khi kết hợp với hai khái niệm khác là Event Sourcing (ES)Domain-Driven Design (DDD). DDD cung cấp một phương pháp luận để phân tích và mô hình hóa các miền nghiệp vụ phức tạp, giúp xác định ranh giới (Bounded Contexts) và các đối tượng nghiệp vụ cốt lõi (Aggregates). Việc áp dụng DDD giúp thiết kế write model trong CQRS một cách hiệu quả, phản ánh chính xác logic nghiệp vụ. Trong khi đó, Event Sourcing là một mẫu kiến trúc lưu trữ hoàn toàn khác biệt. Thay vì lưu trữ trạng thái hiện tại của dữ liệu, ES lưu trữ một chuỗi các sự kiện (events) đã xảy ra theo thứ tự thời gian. Trạng thái hiện tại của một đối tượng được tái tạo bằng cách áp dụng tất cả các sự kiện liên quan đến nó. Khi kết hợp với CQRS, Event Sourcing trở thành cơ chế hoàn hảo để đồng bộ hóa dữ liệu từ write model sang read model. Mỗi khi một Command thay đổi trạng thái, một event được sinh ra và lưu trữ, đồng thời được phát đi để các read model cập nhật. Sự kết hợp này tạo ra một hệ thống mạnh mẽ, linh hoạt, có khả năng kiểm toán (audit) và phục hồi trạng thái tại bất kỳ thời điểm nào trong quá khứ.

4.1. Vai trò của Event Sourcing trong việc đồng bộ hóa dữ liệu CQRS

Event Sourcing giải quyết một trong những thách thức lớn nhất của Fully CQRS: đồng bộ hóa dữ liệu giữa write-side và read-side. Trong mô hình này, kho lưu trữ sự kiện (Event Store) trở thành nguồn chân lý duy nhất (single source of truth). Mỗi Command thành công sẽ tạo ra một hoặc nhiều sự kiện bất biến (immutable events) được ghi vào Event Store. Các sự kiện này sau đó được xuất bản (published) lên một Event Bus. Các thành phần ở phía đọc (read side) sẽ lắng nghe những sự kiện này và sử dụng chúng để xây dựng hoặc cập nhật các read model chuyên biệt của mình. Cách tiếp cận này không chỉ đảm bảo tính nhất quán cuối cùng (Eventual Consistency) một cách đáng tin cậy mà còn cho phép tái tạo lại toàn bộ hoặc một phần read model bất cứ lúc nào bằng cách phát lại (replay) các sự kiện từ Event Store.

4.2. Áp dụng Domain Driven Design DDD để mô hình hóa nghiệp vụ

Domain-Driven Design (DDD) cung cấp bộ công cụ và nguyên tắc để giải quyết các bài toán nghiệp vụ phức tạp. Khi áp dụng vào CQRS, DDD giúp định hình write model một cách rõ ràng. Các khái niệm như Aggregate (một cụm các đối tượng được coi là một đơn vị nhất quán) trở thành đối tượng trung tâm xử lý các Command. Logic nghiệp vụ được gói gọn bên trong các Aggregate, đảm bảo rằng mọi thay đổi trạng thái đều tuân thủ các quy tắc và bất biến của miền. Việc sử dụng Ubiquitous Language (ngôn ngữ chung) của DDD cũng giúp đảm bảo rằng các Command và Event trong hệ thống phản ánh chính xác thuật ngữ kinh doanh, giúp việc giao tiếp giữa đội ngũ phát triển và các bên liên quan trở nên hiệu quả hơn.

V. Phân tích hạn chế và các trường hợp nên áp dụng CQRS

Mặc dù mang lại nhiều lợi ích, phương pháp mô hình hóa CQRS không phải là giải pháp phù hợp cho mọi bài toán. Hạn chế lớn nhất của nó là sự gia tăng độ phức tạp cho hệ thống. Việc phải quản lý hai mô hình, cơ chế đồng bộ hóa dữ liệu, và có thể là cả hai cơ sở dữ liệu riêng biệt đòi hỏi nhiều công sức hơn trong cả quá trình phát triển và vận hành. Một trong những thách thức điển hình là vấn đề tính nhất quán cuối cùng (Eventual Consistency). Do có độ trễ trong việc đồng bộ dữ liệu từ phía ghi sang phía đọc, người dùng có thể không thấy được thay đổi ngay lập tức sau khi thực hiện một hành động. Điều này có thể chấp nhận được trong nhiều hệ thống, nhưng lại là vấn đề nghiêm trọng đối với các ứng dụng yêu cầu tính nhất quán mạnh mẽ, chẳng hạn như hệ thống tài chính. Do đó, việc quyết định áp dụng CQRS cần được cân nhắc kỹ lưỡng dựa trên đặc thù của từng dự án. Mô hình này phát huy hiệu quả tốt nhất trong các miền cộng tác (collaborative domains), các hệ thống có lưu lượng truy cập cao với yêu cầu đọc và ghi không cân bằng, hoặc các hệ thống cần tối ưu hóa các hoạt động truy vấn phức tạp.

5.1. Nhược điểm cần lưu ý Eventual Consistency và độ phức tạp

Eventual Consistency là một hệ quả tự nhiên khi áp dụng Fully CQRS với cơ chế đồng bộ bất đồng bộ. Điều này có nghĩa là hệ thống sẽ nhất quán vào một thời điểm trong tương lai, nhưng có một khoảng trễ ngắn. Lập trình viên phải xử lý các kịch bản mà người dùng truy vấn dữ liệu cũ. Bên cạnh đó, độ phức tạp của hệ thống tăng lên do phải quản lý các thành phần bổ sung như Message Queue, các tiến trình đồng bộ, và logic xử lý giao dịch phân tán (nếu có). Chi phí vận hành (Operational Cost) cũng cao hơn do cần giám sát nhiều dịch vụ hơn.

5.2. Khi nào nên và không nên sử dụng phương pháp mô hình hóa CQRS

Nên sử dụng CQRS trong các trường hợp sau: các hệ thống có lưu lượng đọc rất cao và cần tối ưu hóa truy vấn (ví dụ: trang thương mại điện tử, mạng xã hội); các ứng dụng cho phép nhiều người dùng cộng tác trên cùng một dữ liệu (ví dụ: Google Docs); các hệ thống có logic nghiệp vụ phức tạp chỉ áp dụng cho phía ghi. Ngược lại, không nên sử dụng CQRS cho các ứng dụng CRUD đơn giản, các dự án có đội ngũ phát triển chưa có kinh nghiệm với kiến trúc này, hoặc các hệ thống yêu cầu tính nhất quán mạnh và tức thời trên toàn bộ hoạt động.

5.3. Case study Axel Springer triển khai CQRS thành công với AWS

Nhà xuất bản kỹ thuật số Axel Springer đã áp dụng thành công kiến trúc CQRS để quản lý hàng nghìn hình ảnh. Họ đã triển khai một hệ thống Fully CQRS sử dụng AWS. Cụ thể, DynamoDB được dùng làm kho lưu trữ sự kiện (Event Sourcing), Aurora MySQL Serverless làm read model để phục vụ các truy vấn, và AWS Simple Queue Service (SQS) để xử lý các yêu cầu cập nhật bất đồng bộ. Giải pháp này giúp họ xử lý các tác vụ hàng loạt hiệu quả, cải thiện hiệu năng ứng dụng và nâng cao trải nghiệm người dùng, cho thấy tính ứng dụng thực tiễn và lợi ích to lớn của CQRS trong các hệ thống quy mô lớn.

11/09/2025

Trích đoạn nội dung tài liệu

Chương 1: GIỚI THIỆU TỔNG QUAN 1.1 Tổng quan đề tài 1.1 Ngữ cảnh và dặt vấn đề Khi xây dựng một hệ thống phần mềm lớn, chúng ta thường sẽ phải đối mặt với các thách thức như: - Khả năng mở rộng. - Tính ổn định. - Tính nhất quán của dữ liệu. - Tranh chấp tài nguyên.

- Đòi hỏi hiệu năng cao. Và trong đó, khả năng mở rộng của hệ thống là một trong những thách thức quan trọng nhất cần được giải quyết. Một hệ thống đi vào vận hành cần đảm bảo được khả năng mở rộng của nó khi yêu cầu từ phía người dung hay nghiệp vụ tăng lên. Thông thường, chúng ta thường có ba hướng để có thể scale up hệ thống của mình theo mô hình sau: Ở trục X là khả năng mở rộng theo chiều ngang, khi ta cố gắng tăng số lượng service của mình lên.

Cách thông thường nhất có thể nhắc đến là tạo ra rất nhiều worker service rồi dùng một load balancer để cân bằng tải cho các worker này.O23 Event Sourcing Ở trục Y là khả năng mở rộng theo hướng phân tách các chức năng của hệ thống. Khi này ta cố gắng tách biệt các chức năng cụ thể ra, chia chúng thành các service riêng và dễ dang scale up riêng biệt chúng theo như cầu của từng service. Ở trục Z là khả năng mở rộng hệ thống theo hướng phân mảnh dữ liệu. Tổ chức dữ liệu theo hướng chia nhỏ dữ liệu thành các phân đoạn, phân tán dữ liệu lên các database khác nhau để có thể truy cập một cách hiệu quả.

Và ta có thể nhận thấy rằng, mọi phần mềm đều luôn cần phải lưu một thứ gì đó và cần một thứ gì đó để có thể lưu dữ liệu. Và có thể nói mở rộng theo hướng phân chia dữ liệu là một trong những cách cốt lõi và hiệu quả. Và trong kiến trúc phần mềm thông thường, chúng ta thường sử dụng mô hình CRUD để truy xuất dữ liệu. Với CRUD, chúng ta thường chỉ dung một model duy nhất cho tất cả các thao tác truy cập đến dữ liệu.

Mô hình CRUD tỏ ra là rất hiệu quả với các hệ thống nhỏ, không qua phức tạp và không yêu cầu về mặt kĩ thuật quá cao. Tuy nhiên, đối với một hệ thống lớn, mô hình CRUD tỏ ra không hiệu quả và tỏ ra các khó khăn khi: - Chỉ sử dụng cùng một model cho tất cả nghiệp vụ - Các logic nghiệp vụ luôn phát sinh và ngày càng trở nên phức tạp, dẫn tới các model cùng trở nên phức tạp để có thể vừa đáp ứng nhu cầu đọc ghi. - Các nghiệp vụ ghi dữ liệu quan trọng luôn cần sự chính xác và chặt chẽ. - Các nghiệp vụ đọc dữ liệu thì linh hoạt và nhanh.

- Khó khăn trong việc scale độc lập yêu cầu hiệu năng cho phần đọc và ghi - Dùng chung một mô hình, một database có thể dẫn đến tranh chấp dữ liệu khi đọc ghi. Ví dụ cụ thể: Trong một hệ thống ngân hàng, thông thường ta xây dựng chức năng cho phép người dùng xem số dư tài khoản, rút tiền và chuyển tiền vào ngân hàng. Tuy nhiên trong thực tế, chức năng được người dùng sử dụng với tần suất lớn nhất đó chính là chức năng xem số dư tài khoản, cụ thể như sau: Trang 3 SE101.O23 Event Sourcing - Khi người dùng rút tiền, điều quan trọng nhất mà người dùng thực sự quan tâm đến là xem tài khoản có còn đủ số dư để rút tiền hay không, chứ không phải là các giao dịch trước và sau khi rút tiền xong. - Khi người dùng chuyên tiền vào ngân hàng, thứ mà người dùng quan tâm sau khi giao dịch chính là số dư trong tài khoản sau khi đã chuyển tiền vào, chứ không phải thông tin chi tiết các giao dịch trước và sau khi thực hiện chuyển tiền.

Với các lý do trên, hợp lý nhất là ta sẽ lưu một cột dữ liệu có tên “balance” vào trong bảng account của người dùng, nhưng như vậy sẽ phát sinh ra một số vấn đề: - Khi có một giao dịch mới, giả dụ như chuyển tiền, quy trình thực hiện sẽ là:  Đọc cột balance => Nếu đủ tiền => Tạo record transaction mới => Cập nhật lại cột “balance” trong bảng account. Điều này sẽ gây nên trì trệ về mặt hiệu suất, do người dùng phải đợi hệ thống thực hiện hết một loạt các quy trình trên mỗi khi thực hiện chuyển tiền, đồng thời việc thực hiện quá nhiều việc một lúc sẽ gây nên sự phức tạp trong thao tác ghi dữ liệu, gây phức tạp và khó bảo trì về sau. Vô hình chung, việc áp dụng giải pháp CRUD trong trường hợp này thực ra là đang làm phức tạo hóa việc ghi để cải thiện hiệu suất cho việc đọc. Để giải quyết các khiêm khuyết đó thì mô hình CQRS đã được ra đời.O23 Event Sourcing Chương 2: KIẾN THỨC NỀN TẢNG 2.1 CQRS là gì CQRS được giới thiệu lần đầu tiên bới Greg Young vào năm 2006 Command and Query Responsibility Segregation uses the same definition of Commands and Queries that Meyer used and maintains the viewpoint that they should be pure.

The fundamental difference is that in CQRS objects are split into two objects, one containing the Commands one containing the Queries. Greg Young CQRS hay Command and Query Responsibility Segregation là một parttern mô tả sự phân chia trách nhiệm của các lệnh (các thao tác ghi) và các truy vấn (các thao tác đọc) trên một hệ thống. Điều đó có nghĩa là các logic của hệ thông được chia ra làm hai theo chiều dọc. Tách biệt các hành vi làm thay đổi hệ thống ra khỏi các thao tác truy xuất dữ liệu.

Và đối với định nghĩa ban đầu của CQRS, các đối tượng được phân chia riêng biệc cho việc đọc và ghi không phải đối với việc kết nối và truy xuất dữ liệu, và là đối với các quy trình xữ lý, nơi mà các logic kinh doanh được thực hiện, chứ không phải là sụ lưu trữ riêng biệc.2 CQS Và nhắc đến CQRS mà không nhắc đến CQS là một sự thiếu xót. CQS là một mẫu thiết kế, và nó là cụm từ viết tắt của Command Query Separation (Tách biệt Command và Query). CQS là khái niệm cốt lõi định nghĩa hai loại hoạt động xử lý trong một hệ thống: một command thực hiện một công việc, và một query trả về thông tin, và không bao giờ có một chức năng đảm nhận cả hai nhiệm vụ này. Thuật ngữ này được tạo ra bởi Bertrand Meyer trong cuốn sách của ông "Object- oriented Software Construction" (1988, Prentice Hall).

Ông đã tạo ra nó trong quá trình làm việc với ngôn ngữ lập trình Eiffel. CQRS lấy nguyên tắc định nghĩa của CQS và mở rộng nó cho các đối tượng cụ thể trong hệ thống, một đối tượng để truy xuất dữ liệu và một đối tượng để sửa đổi dữ liệu. CQRS là mẫu kiến trúc rộng hơn, và CQS là nguyên tắc tổng quát về hành vi.3 Command Trong CQRS, chúng ta có hai khái niệm hết sức quan trọng, cần được làm rõ và phân biệt chính xác. Đó là Command và Query.

Việc ghi dữ liệu được gọi là lệnh (command) là bất cứ thao tác nào làm thay đổi trạng thái của hệ thống. - Trong đã số các trường hợp thì chúng không nhất thiết phải trả về dữ liệu. - Các command có thể được thực hiện đồng bộ hoặc bất đồng bộ. - Các command có thể được truyền tải thông qua message bus hoặc không.

Flow của một command có thể được mô tả như sau: Trang 6 SE101.O23 Event Sourcing Lưu ý:  Nếu các command được thực hiện đồng bộ, kết quả chỉ nên đơn giản được trả về là OK hoặc không, mà không bao gồm chi tiết dữ liệu được thay đổi như thế nào (là phần công việc của read model)  Nếu được thực hiện bất đồng bộ, thì gửi một commit lên message infrastructure. Các command nên được tuân thủ theo quy tắc fire & forget, hay hiểu là chúng ta chỉ cần ra lệnh và không cần quan tâm đến kết quả của việc đó ngay lập tức. Chúng ta không đòi hỏi thêm bất kì một thông tin nào từ một command ngoại trừ việt kết quả của nó là thành công hay thất bại. Bơi các command có thể được thực hiện đồng bộ hoặc không đồng bộ, và không thể nào đảm bảo được việc các thay đổi có thể được Trang 7 SE101.O23 Event Sourcing thực hiện hoàn tất một cách tức thời.

Kết quả của quá trình thực hiện command có thể được biết sau đó thông qua một lệnh đọc. Việc áp dụng quy tắc “fire & forget” không chỉ giúp hệ thống giúp giảm áp lực (thông qua việc không đòi hỏi tốc độ đáp ứng cao) mà nó còn có thể giúp cải thiện về phần trải nghiệm của người dùng, bằng cách không yêu cầu họ phải cố gắng đợi phản hồi kết quả từ hệ thống. Ví dụ như hệ thống TikTok, khi TikToker cần upload một video mới lên, thì thay vì sử dụng “fire and waiting for response”, tức là gửi video len server, và phải đợi server thực hiện một loạt các quy trình từ xác thực video, lưu các value quan trong, thực hiện tính toán nghiệp vụ,. thì ta nên thực hiện “fire & forget” sẽ giúp cải thiện trải nghiệm người dùng, tức là TikToker chỉ cần bấm nút “PUSH VIDEO” và xong, tắt ứng dụng, nếu cần thiết phía server sẽ gửi push notification cho người dùng sau đó khi thực hiện xong công việc.4 Query Khác với Command, query không được thay đổi trạng thái của dữ liệu, mà là thao tác chỉ đọc.

Các command có thể gọi đến các command hoặc các query khác để hoàn tất công việc của mình. Trong khi đó các query thì được phép gọi đến các query khác, bởi trong trường hợp một query gọi đến một command, thì bản chất, nó đã trở thành một command. Ví dụ: tìm kiếm sản phẩm, lấy thông tin chi tiết về đơn hàng, kiểm tra số dư tài khoản. Tóm lại: Với CQRS nó đơn giản phân tách nhiệm vụ đọc và ghi dữ liệu cho các model riêng biệt, nghĩa là khi chúng ta thực hiện thay đổi dữ liệu thì sẽ tương tác với một model và khi đọc sẽ là một model khác.

Chúng ta có thể sử dụng nhiều model đọc dữ liệu khác nhau cho các mục đích riêng biệt.5 Base model and work flow Với CQRS nó đơn giản phân tách nhiệm vụ đọc và ghi dữ liệu cho các model riêng biệt, nghĩa là khi chúng ta thực hiện thay đổi dữ liệu thì sẽ tương tác với một model và khi đọc sẽ là một model khác. Chúng ta có thể sử dụng nhiều model đọc dữ liệu khác nhau cho các mục đích riêng biệt. Và trong thực tế CQRS có thể được triển khai dưới hai dạng: 1.

Nội dung được bảo vệ bản quyền — Tải xuống đầy đủ