📜  cqrs 设计模式 .net core - C# (1)

📅  最后修改于: 2023-12-03 14:40:15.241000             🧑  作者: Mango

CQRS 设计模式 .NET Core - C#


CQRS(Command Query Responsibility Segregation)是一种在应用程序中使用的设计模式,它将应用程序的读和写的操作分离开来,从而使应用程序更易于扩展。

在 .NET Core 中,我们可以使用 CQRS 模式来帮助我们构建一个清晰、可维护和可扩展的应用程序。下面将介绍一些关键概念和代码片段,来帮助我们更好地理解 CQRS。

1. 命令(Command)

命令是一种操作请求,用于修改应用程序的状态。在 CQRS 中,我们通常将命令分为两种:

  • 同步命令:会阻塞线程,并在操作执行完后返回结果。
  • 异步命令:不会阻塞线程,操作执行完后会通知调用方。

下面是一个示例同步命令的代码片段:

public class CreateProductCommandHandler : ICommandHandler<CreateProductCommand, Guid>
{
    private readonly IProductRepository _productRepository;

    public CreateProductCommandHandler(IProductRepository productRepository)
    {
        _productRepository = productRepository;
    }

    public Guid Handle(CreateProductCommand command)
    {
        var product = new Product(command.Name, command.Description, command.Price);
        _productRepository.Add(product);
        _productRepository.SaveChanges();

        return product.Id;
    }
}

在这个例子中,CreateProductCommandHandler 是一个同步命令处理器,它接收一个 CreateProductCommand 命令对象,并返回一个 Guid 类型的结果。这个命令的作用是创建一个新的产品并保存到数据库中。

2. 查询(Query)

查询是一种操作请求,用于获取应用程序的状态。在 CQRS 中,我们通常将查询分为两种:

  • 同步查询:会阻塞线程,并在查询执行完后返回结果。
  • 异步查询:不会阻塞线程,查询执行完后会通知调用方。

下面是一个示例同步查询的代码片段:

public class GetProductQueryHandler : IQueryHandler<Guid, ProductDto>
{
    private readonly IProductRepository _productRepository;

    public GetProductQueryHandler(IProductRepository productRepository)
    {
        _productRepository = productRepository;
    }

    public ProductDto Handle(Guid productId)
    {
        var product = _productRepository.GetById(productId);

        return new ProductDto(product.Id, product.Name, product.Description, product.Price);
    }
}

在这个例子中,GetProductQueryHandler 是一个同步查询处理器,它接收一个 Guid 类型的产品 ID,并返回一个 ProductDto 类型的结果。这个查询的作用是获取一个产品的详细信息。

3. 事件(Event)

事件是一种消息,用于通知其他组件应用程序的状态已经发生变化。在 CQRS 中,我们通常使用事件来实现松耦合和异步通信。

下面是一个示例事件的代码片段:

public class ProductCreatedEvent : IEvent
{
    public Guid ProductId { get; }
    public string Name { get; }
    public string Description { get; }
    public decimal Price { get; }

    public ProductCreatedEvent(Guid productId, string name, string description, decimal price)
    {
        ProductId = productId;
        Name = name;
        Description = description;
        Price = price;
    }
}

在这个例子中,ProductCreatedEvent 是一个事件,它包含了新创建产品的详细信息,并提供了一个构造函数来初始化这些信息。

4. 聚合(Aggregate)

聚合是一种将多个相互关联的实体组合在一起的方式,在 CQRS 中,我们通常使用聚合来封装业务逻辑和保护实体的状态。

下面是一个示例聚合的代码片段:

public class Product : AggregateRoot<Guid>
{
    public string Name { get; private set; }
    public string Description { get; private set; }
    public decimal Price { get; private set; }

    private Product() { }

    public Product(string name, string description, decimal price)
    {
        Id = Guid.NewGuid();
        Name = name;
        Description = description;
        Price = price;

        AddDomainEvent(new ProductCreatedEvent(Id, Name, Description, Price));
    }
}

在这个例子中,Product 是一个聚合,它包含了产品的详细信息,并提供了一个构造函数来创建新产品。注意,Product 派生自 AggregateRoot 类,该类是一个基类,用于实现聚合根的基本行为。

5. 命令/查询处理器(Command/Query Handler)

命令/查询处理器是一种处理命令或查询的组件,在 CQRS 中,我们通常使用命令/查询处理器来编写业务逻辑。

下面是一个示例命令处理器和查询处理器的代码片段:

public interface ICommandHandler<in TCommand, out TResult>
    where TCommand : ICommand<TResult>
{
    TResult Handle(TCommand command);
}

public interface IQueryHandler<in TQuery, out TResult>
    where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

在这个例子中,ICommandHandlerIQueryHandler 是两个接口,分别用于定义命令处理器和查询处理器的通用接口。每个处理器都需要实现这个接口,并提供一个 Handle 方法,用于处理命令或查询请求。

6. 命令总线(Command Bus)

命令总线是一种用于将命令分发给命令处理器的消息传递机制,在 CQRS 中,我们通常使用命令总线来解耦命令和命令处理器之间的依赖关系。

下面是一个示例命令总线的代码片段:

public interface ICommandBus
{
    Task<TResult> ExecuteAsync<TResult>(ICommand<TResult> command);
}

public class CommandBus : ICommandBus
{
    private readonly IServiceProvider _serviceProvider;

    public CommandBus(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public async Task<TResult> ExecuteAsync<TResult>(ICommand<TResult> command)
    {
        var handlerType = typeof(ICommandHandler<,>)
            .MakeGenericType(command.GetType(), typeof(TResult));

        dynamic handler = _serviceProvider.GetService(handlerType);

        return await handler.Handle((dynamic)command);
    }
}

在这个例子中,ICommandBus 是一个接口,用于定义命令总线的通用接口。CommandBus 是一个实现了 ICommandBus 接口的类,用于将命令分发给正确的命令处理器。

7. 事件总线(Event Bus)

事件总线是一种用于将事件分发给事件处理器的消息传递机制,在 CQRS 中,我们通常使用事件总线来解耦事件和事件处理器之间的依赖关系。

下面是一个示例事件总线的代码片段:

public interface IEventBus
{
    void Publish(IEvent @event);
}

public class EventBus : IEventBus
{
    private readonly IServiceProvider _serviceProvider;

    public EventBus(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void Publish(IEvent @event)
    {
        var eventType = @event.GetType();

        var handlersType = typeof(IEventHandler<>)
            .MakeGenericType(eventType);

        var handlers = _serviceProvider.GetServices(handlersType);

        foreach (dynamic handler in handlers)
        {
            handler.Handle((dynamic)@event);
        }
    }
}

在这个例子中,IEventBus 是一个接口,用于定义事件总线的通用接口。EventBus 是一个实现了 IEventBus 接口的类,用于将事件分发给正确的事件处理器。

结论

CQRS 设计模式提供了一种规范的方式来组织我们的应用程序,从而使其更易于理解、维护和扩展。在 .NET Core 中,我们可以使用 CQRS 模式来构建一个干净、可测试和可扩展的应用程序,也可以使用基于 CQRS 的架构来构建分布式系统。