您有一个正确的观点,这不是实现 CQRS 的非常典型的方法。对我来说,它看起来更像是一个单一的方法存储库。虽然 CQRS 的主要目标是分离查询和命令,但从长远来看,您采用的这种方法可能会使事情复杂化。
无论是查询还是命令,CQRS 的典型实现是您拥有包含执行此查询所需的所有信息的查询(或命令)对象 + 处理此查询的处理程序。检查下面的示例。
public class OrdersQuery
{
public DateTime? FromDate { get; }
public DateTime? ToDate { get; }
public OrdersQuery(DateTime? fromDate, DateTime? toDate)
{
FromDate = fromDate;
ToDate = toDate;
}
}
public class OrdersQueryHandler
{
public List<Order> Handle(OrdersQuery query)
{
//Implementation Goes Here
}
}
在此示例中,您的 OrdersQuery 包含可选的 FromDate 和 ToDate 过滤器(这仅用于演示),它应该包含查询处理程序执行查询所需的任何输入数据。或者根本没有数据,以防您想在一个表中返回所有数据,例如查找表(例如 GetOrdersTypes、国家/地区)
要使用此查询,请执行以下操作。
public static class Program
{
public static void Main(string[] args)
{
var handler = new OrdersQueryHandler();
var orders = handler.Handle(new OrdersQuery(DateTime.Now.AddDays(-30), DateTime.Now));
}
}
但您可能已经看到了问题,在实际应用中,您将需要使用依赖注入、工厂或某种可以轻松使用查询处理程序的方式。
你不能每次需要使用一个新的 SomeHandler() ,这将创建对处理程序的硬依赖,并使代码更难维护。如果您采用您在问题中建议的方法,您最终会为每个查询/命令处理程序提供 1 个接口。想想在某个时候需要注入多少接口来执行一些复杂的业务规则。
CQRS 和 Mediator 模式很好地结合在一起解决了这些问题,在我看来,MediatR 是最好的库,可以达到这个目的,而且它一点也不复杂。检查下面的示例。
public class OrdersQuery : IRequest<List<Order>>
{
public DateTime? FromDate { get; }
public DateTime? ToDate { get; }
public OrdersQuery(DateTime? fromDate, DateTime? toDate)
{
FromDate = fromDate;
ToDate = toDate;
}
}
public class OrdersQueryHandler : IRequestHandler<OrdersQuery, List<Order>>
{
public Task<List<Order>> Handle(OrdersQuery request, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
public static class Program
{
public static async Task Main(string[] args)
{
var services = new ServiceCollection()
.AddMediatR(typeof(Program).Assembly)
.BuildServiceProvider();
var mediator = services.GetRequiredService<IMediator>();
var orders = await mediator.Send(new OrdersQuery(DateTime.Now.AddDays(-30), DateTime.Now));
}
}
在这里,您的查询将实现 IRequest,其中 T 是结果,您的查询处理程序将实现 IRequestHandler,其中 T 是查询类型,TResult 是结果(例如,列表)(注意:TResult 必须与IRequest 中的类型参数)。在此示例中,我使用的是 MS 依赖注入,但您可以使用您选择的 DI 库。
MediatR 在这里扫描所有处理程序的程序集(如果需要,您可以手动注册它们)。现在要使用它,您只需要了解 IMediator 接口,无需再了解 IOrdersQuery、IOrdersCommand 等。您将只引用 IMediator,中介将负责为正确的查询解析正确的处理程序。
现在是关于是否应该通过 HTTP 对资源使用 CQRS 的问题。如果 CQRS 是指这里与中介讨论的模式。是的,你绝对可以做到。
但 CQRS 最常指的是数据库查询/命令。有时人们也实施 CQRS 来对不同的数据库进行读写。因此,为了避免混淆,只需将其称为中介模式,它肯定适用于 Http。