0Pricing
RabbitMQ Messaging & Async Systems · 课时

命令查询职责分离(CQRS)

在应用程序中使用 RabbitMQ 应用 CQRS 模式,将读操作与写操作分离。提升数据密集型系统的可扩展性和性能。

命令查询职责分离(CQRS) 是 CoddyKit 上的免费 RabbitMQ Messaging & Async Systems 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 RabbitMQ Messaging & Async Systems 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 RabbitMQ Messaging & Async Systems 课程共包含 4 节课。

本课时的部分内容尚未翻译,以英文显示。

What is CQRS?

Ever wished your application could handle tons of writes and reads without slowing down? That's where CQRS comes in! It stands for Command-Query Responsibility Segregation.

CQRS is an architectural pattern that separates the operations for reading data from the operations for updating data. Think of it as having two specialized teams: one for taking orders and one for answering questions.

Understanding Commands

The "Command" side handles all requests that change the state of your application. These are actions like "CreateProduct", "UpdateOrderStatus", or "AddUser".

  • Commands are imperative: They tell the system to do something specific.
  • Commands are processed: They go through handlers that validate and execute the requested change.
  • Commands often trigger events: After a command is successfully processed, an event might be published.

Understanding Queries

The "Query" side is all about retrieving data. These are requests like "GetProductDetails", "ListAllOrders", or "FindUsersByLocation".

  • Queries are declarative: They ask for information without changing anything.
  • Queries use optimized models: Data is often stored in a read-optimized format, perfect for fast retrieval.
  • Queries return data: They provide the information requested by the user interface or other services.

Benefits of CQRS

Separating commands and queries offers several advantages, especially in complex systems:

  • Scalability: You can scale read and write services independently. Read models often get more traffic.
  • Performance: Read models can be highly optimized for queries (e.g., de-normalized data, different databases).
  • Flexibility: Different data stores can be used for reads (e.g., NoSQL for speed) and writes (e.g., SQL for consistency).
  • Simplicity: Each model is simpler, focused on its specific task.

RabbitMQ's Role in CQRS

RabbitMQ is an excellent fit for implementing CQRS, particularly for the command side. When a command is issued, it can be published as a message to a RabbitMQ queue.

Consumers (command handlers) then pick up these messages and execute the business logic to update the write model. This makes command processing asynchronous and decoupled.

Producer: Update Product Name

Let's imagine we want to update a product's name. We'll send a "UpdateProductNameCommand" message to RabbitMQ. Here's a simple Java producer example:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class CommandProducer {
    private final static String QUEUE_NAME = "product_commands";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost"); // Assuming RabbitMQ is local

        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {

            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            String commandJson = "{\"commandType\":\"UpdateProductName\", \"productId\":\"P123\", \"newName\":\"New Awesome Product\"}";
            channel.basicPublish("", QUEUE_NAME, null, commandJson.getBytes("UTF-8"));
            System.out.println(" [x] Sent command: '" + commandJson + "'");
        }
    }
}

Consumer: Process Product Update

On the other side, a consumer service (our command handler) listens for these commands. When it receives an "UpdateProductName" command, it updates the authoritative write model (e.g., a SQL database).

This consumer represents the "write" side of our CQRS architecture.

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;

public class CommandConsumer {
    private final static String QUEUE_NAME = "product_commands";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");

        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        System.out.println(" [*] Waiting for commands. To exit press CTRL+C");

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received command: '" + message + "'");
            // In a real app, parse JSON, validate, update write model (e.g., database)
            System.out.println(" [x] Product write model updated for: " + message.split(":")[2].split(",")[0]);
        };
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
    }
}

Synchronizing Read Models

After the write model is updated, how does the read model get the new data? This is often done by publishing events.

When a product name changes, the command handler can publish a "ProductNameUpdatedEvent" to another RabbitMQ exchange. A separate service (a projector or denormalizer) subscribes to this event and updates the read-optimized data store.

  • Write Model: Optimized for transactional consistency.
  • Read Model: Optimized for query performance.

Fast Data Retrieval

With the read model now updated, client applications can query it directly. Since this model is specifically designed for reads, queries are often much faster and simpler.

For example, a product catalog service would query this read model to display product details, without ever touching the complex transactional write model.

CQRS Core Principle

Consider the architecture we've discussed. What is the primary benefit of separating read and write models in CQRS?

CQRS: Scalability & Performance

In this lesson, you learned about Command-Query Responsibility Segregation (CQRS). We saw how it separates data modification (commands) from data retrieval (queries), often using different data models.

RabbitMQ plays a crucial role by enabling asynchronous processing of commands, allowing for independent scaling and optimization of your application's read and write functionalities. This pattern is powerful for data-intensive and high-performance systems.

常见问题解答

「命令查询职责分离(CQRS)」课时是免费的吗?

是的 — 「命令查询职责分离(CQRS)」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 RabbitMQ Messaging & Async Systems 课程的其余内容,请升级到 CoddyKit PRO。 RabbitMQ Messaging & Async Systems 课程共包含 4 节课。

「命令查询职责分离(CQRS)」这节课中我会学到什么?

在应用程序中使用 RabbitMQ 应用 CQRS 模式,将读操作与写操作分离。提升数据密集型系统的可扩展性和性能。 你通过在浏览器中直接运行的动手代码来练习 RabbitMQ Messaging & Async Systems,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 RabbitMQ Messaging & Async Systems 需要有经验吗?

无需任何先前经验。CoddyKit 上的 RabbitMQ Messaging & Async Systems 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「命令查询职责分离(CQRS)」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 RabbitMQ Messaging & Async Systems 课中编写并运行代码吗?

能。每节 RabbitMQ Messaging & Async Systems 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 消息处理中的幂等性
  2. 使用 RabbitMQ 实现 Saga 模式
  3. 命令查询职责分离(CQRS)
  4. 可靠发布的发件箱模式
← 返回 RabbitMQ Messaging & Async Systems