Asp.net core webapi CQRS设计模式的正确实现?

Asp.net core webapi CQRS设计模式的正确实现?,asp.net-core-webapi,cqrs,mediatr,Asp.net Core Webapi,Cqrs,Mediatr,我正在开发一些使用CQRS设计模式的遗留应用程序(.Net核心和框架应用程序) 基本上,我们有一个使用Dapper的数据访问层存储库。然后,我们通过创建两个不同的接口来应用CQR,一个用于命令,一个用于查询。这些接口看起来像: //查询接口 公共接口IOrderQuery { 订单GetOrderById(int-id); } //命令界面 公共接口IOrderCommand { bool PlaceOrder(订单placedOrder) } 在我看来,这可能不是CQR的正确实现,因为我们

我正在开发一些使用CQRS设计模式的遗留应用程序(.Net核心和框架应用程序)

基本上,我们有一个使用Dapper的数据访问层存储库。然后,我们通过创建两个不同的接口来应用CQR,一个用于命令,一个用于查询。这些接口看起来像:

//查询接口

公共接口IOrderQuery { 订单GetOrderById(int-id); } //命令界面

公共接口IOrderCommand
{
bool PlaceOrder(订单placedOrder)
}
在我看来,这可能不是CQR的正确实现,因为我们对输入和输出使用相同的模型,并且每个操作都没有特定的处理程序。然而,我的主要开发人员说,这是正确的方法,因为我们只需要将命令和查询分开,然后归档CQRS模式。我现在很困惑

此外,我们正在对web api应用相同的方法,其中存储库使用httpClient

一、 因此,建议使用MediatR应用CQR和DDD,但看起来他们不想使用它,因为它太复杂了

我只是一个初级开发人员,所以有人能为我解释一下,我们目前使用的方法是CQR的正确实现吗?我应该在HttpClient上应用CQR吗?使用CQR的好处是什么


谢谢

您有一个正确的观点,这不是实现CQR的典型方法。在我看来,它更像是一个单一的方法存储库。虽然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
    }
}
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));
    }
}
但您可能已经看到了这个问题,在现实世界的应用程序中,您将需要使用依赖项注入、工厂或某种方式,以便于使用查询处理程序

您不能每次需要使用新的SomeHandler(),这将创建对处理程序的硬依赖,并使代码更难维护。如果您采用问题中建议的方法,那么每个查询/命令处理程序将有一个接口。想一想在某个时刻需要注入多少接口来执行一些复杂的业务规则

CQR和Mediator模式在解决这类问题方面发挥了很好的作用,在我看来,MediatR是最好的库,它一点也不复杂。检查下面的示例

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
    }
}
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));
    }
}
公共类OrdersQuery:IRequest
{
公共日期时间?从日期{get;}
公共日期时间?ToDate{get;}
公共订单查询(DateTime?fromDate,DateTime?toDate)
{
FromDate=FromDate;
ToDate=ToDate;
}
}
公共类OrdersQueryHandler:IRequestHandler
{
公共任务句柄(OrdersQuery请求、CancellationToken CancellationToken)
{
抛出新的NotImplementedException();
}
}
公共静态类程序
{
公共静态异步任务主(字符串[]args)
{
var services=newservicecolection()
.AddMediatR(类型(程序).Assembly)
.BuildServiceProvider();
var mediator=services.GetRequiredService();
var orders=wait mediator.Send(new OrdersQuery(DateTime.Now.AddDays(-30),DateTime.Now));
}
}
这里,您的查询将实现IRequest,其中T是结果,您的查询处理程序将实现IRequestHandler,其中T是查询类型,TResult是结果(例如,List)(注意:TResult必须与IRequest中的类型参数匹配)。在本例中,我使用MS依赖项注入,但您可以使用您选择的DI库

MediatR在此扫描程序集中的所有处理程序(如果需要,可以手动注册它们)。现在要使用它,您只需要了解IMediator接口,不再需要IORDersky、IOrdersCommand等等。您将只引用IMediator,中介将负责为正确的查询解析正确的处理程序

现在讨论是否应该通过HTTP为资源使用CQR的问题。如果说CQR,您指的是这里与调解人讨论的模式。是的,你肯定能做到


但CQR通常指的是数据库查询/命令。有时人们也会实现CQR来对不同的数据库进行读写操作。因此,为了避免混淆,只需将其称为中介模式,它肯定适用于Http。

您有一个正确的观点,这不是实现CQR的典型方法。在我看来,它更像是一个单一的方法存储库。虽然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
    }
}
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));
    }
}
但您可能已经看到了这个问题,在现实世界的应用程序中,您将需要使用依赖项注入、工厂或某种方式,以便于使用查询处理程序

您不能每次需要使用新的SomeHandler(),这将创建对处理程序的硬依赖,并使代码更难维护。如果您采用问题中建议的方法,那么每个查询/命令处理程序将有一个接口。想一想在某个时刻需要注入多少接口来执行一些复杂的业务规则

CQR和Mediator模式在解决这类问题方面发挥了很好的作用,在我看来,Mediator是最好的库,可以实现这一目的,而且目前并不复杂