一、啥是 CQRS 模式

在软件开发里,咱们经常会碰到业务逻辑很复杂的情况。要是把所有的业务逻辑都搅和在一起,代码就会变得乱糟糟,又难维护又难扩展。这时候,CQRS 模式就登场啦!

CQRS 就是命令查询职责分离(Command Query Responsibility Segregation)的缩写。简单来说,它把系统的操作分成了两类:命令(Command)和查询(Query)。命令是用来改变系统状态的,像创建、更新、删除这些操作;查询呢,就是用来获取系统数据的,不改变系统状态。这么一分离,代码结构就清晰多啦。

比如说,咱们有一个博客系统。当用户发表一篇新博客时,这就是一个命令操作,会改变系统的状态(数据库里多了一篇博客);而当用户查看博客列表时,这就是一个查询操作,只是从数据库里读取数据,不会改变系统状态。

二、MediatR 是个啥

MediatR 是一个轻量级的中介者模式实现库,在 .NET Core 里用得挺多。它能帮咱们实现消息的发布和处理,让不同的组件之间解耦。在 CQRS 模式里,MediatR 可以用来处理命令和查询消息。

举个例子,就好比一个公司里有很多部门,每个部门都有自己的职责。当有一个任务来了,不需要直接和具体的部门沟通,而是把任务交给一个中介(就像 MediatR),由中介把任务分配给合适的部门去处理。这样各个部门之间就不用互相依赖,代码也更好维护。

三、在 .NET Core 里结合 MediatR 实现 CQRS 模式

1. 创建项目

首先,咱们得创建一个 .NET Core 项目。打开命令行工具,输入下面的命令:

// C# 技术栈
dotnet new console -n CQRSExample
cd CQRSExample

这就创建了一个名为 CQRSExample 的控制台项目。

2. 安装 MediatR 包

在项目里安装 MediatR 和 MediatR.Extensions.Microsoft.DependencyInjection 包,命令如下:

// C# 技术栈
dotnet add package MediatR
dotnet add package MediatR.Extensions.Microsoft.DependencyInjection

3. 定义命令和查询

咱们来定义一个简单的命令和查询。假设咱们有一个用户管理系统,要实现创建用户和查询用户列表的功能。

先定义创建用户的命令:

// C# 技术栈
using MediatR;

// 定义创建用户的命令类
public class CreateUserCommand : IRequest<int>
{
    public string Name { get; set; }
    public string Email { get; set; }

    public CreateUserCommand(string name, string email)
    {
        Name = name;
        Email = email;
    }
}

这里的 IRequest<int> 表示这个命令会返回一个整数,通常可以用来表示操作的结果(比如用户的 ID)。

再定义查询用户列表的查询:

// C# 技术栈
using MediatR;
using System.Collections.Generic;

// 定义查询用户列表的查询类
public class GetUsersQuery : IRequest<List<string>>
{
}

这里的 IRequest<List<string>> 表示这个查询会返回一个字符串列表,就是用户的名称列表。

4. 实现命令和查询的处理程序

接下来,要实现命令和查询的处理程序。

先实现创建用户命令的处理程序:

// C# 技术栈
using MediatR;
using System.Threading;
using System.Threading.Tasks;

// 创建用户命令的处理程序
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, int>
{
    public async Task<int> Handle(CreateUserCommand request, CancellationToken cancellationToken)
    {
        // 这里可以实现具体的创建用户逻辑,比如插入数据库
        // 为了简单,我们直接返回一个随机的用户 ID
        return new Random().Next(1, 100);
    }
}

再实现查询用户列表的处理程序:

// C# 技术栈
using MediatR;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

// 查询用户列表的处理程序
public class GetUsersQueryHandler : IRequestHandler<GetUsersQuery, List<string>>
{
    public async Task<List<string>> Handle(GetUsersQuery request, CancellationToken cancellationToken)
    {
        // 这里可以实现具体的查询用户列表逻辑,比如从数据库读取
        // 为了简单,我们直接返回一个固定的用户列表
        return new List<string> { "User1", "User2", "User3" };
    }
}

5. 配置 MediatR

Program.cs 里配置 MediatR:

// C# 技术栈
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var serviceCollection = new ServiceCollection();
        // 注册 MediatR
        serviceCollection.AddMediatR(typeof(Program));

        var serviceProvider = serviceCollection.BuildServiceProvider();
        var mediator = serviceProvider.GetService<IMediator>();

        // 发送创建用户命令
        var createUserCommand = new CreateUserCommand("John Doe", "johndoe@example.com");
        var userId = await mediator.Send(createUserCommand);
        Console.WriteLine($"User created with ID: {userId}");

        // 发送查询用户列表查询
        var getUsersQuery = new GetUsersQuery();
        var users = await mediator.Send(getUsersQuery);
        Console.WriteLine("Users:");
        foreach (var user in users)
        {
            Console.WriteLine(user);
        }
    }
}

四、应用场景

1. 大型复杂业务系统

在大型的业务系统里,业务逻辑非常复杂,不同的业务模块之间交互频繁。使用 CQRS 模式可以把不同的业务逻辑分离,让代码更清晰,更易于维护和扩展。比如电商系统,订单管理、库存管理、用户管理等模块都可以采用 CQRS 模式。

2. 高并发系统

在高并发的系统里,查询和命令的性能需求不一样。查询操作通常需要快速响应,而命令操作可能需要更多的事务处理。CQRS 模式可以分别优化查询和命令的性能,提高系统的整体性能。比如在线游戏系统,玩家的查询操作(如查看排行榜)和命令操作(如购买道具)可以分开处理。

五、技术优缺点

优点

  • 代码清晰:把命令和查询分离,代码结构更清晰,每个模块的职责更明确,易于理解和维护。
  • 可扩展性强:当业务需求变化时,可以很方便地添加新的命令和查询处理程序,而不会影响其他模块。
  • 性能优化:可以分别对查询和命令进行性能优化,提高系统的整体性能。

缺点

  • 复杂度增加:引入 CQRS 模式会增加系统的复杂度,需要更多的代码和设计工作。
  • 数据一致性问题:由于命令和查询分离,可能会出现数据不一致的情况,需要额外的处理来保证数据的一致性。

六、注意事项

1. 数据一致性

在 CQRS 模式里,要特别注意数据一致性问题。可以采用一些策略,比如最终一致性,让数据在一定时间内达到一致。

2. 日志和监控

由于系统变得更复杂,需要加强日志和监控,及时发现和解决问题。

3. 性能测试

在开发过程中,要进行充分的性能测试,确保系统的性能满足需求。

七、文章总结

通过在 .NET Core 里结合 MediatR 实现 CQRS 模式,咱们可以把复杂的业务逻辑清晰地分离,让代码更易于维护和扩展。虽然 CQRS 模式有一些缺点和需要注意的地方,但在合适的应用场景下,它能带来很大的好处。希望大家通过这篇文章,对 CQRS 模式和 MediatR 有了更深入的理解,能在实际项目中灵活运用。