Interface CQRS模式-接口

Interface CQRS模式-接口,interface,cqrs,Interface,Cqrs,我不熟悉CQRS模式,但我想了解为什么您应该使用两个接口: public interface IQuery{} public interface ICommand{} 而不仅仅是一个接口(例如IExecutable,或其他什么…) 而您也有一个处理程序(例如IExecutionHandler,或其他什么…) 如果需要,您仍然可以将其拆分为ICommandExecutionHandler和IQueryExecutionHandler 更新:一次尝试 接下来的代码只是我如何看待它的一个示例。有可

我不熟悉CQRS模式,但我想了解为什么您应该使用两个接口:

public interface IQuery{}
public interface ICommand{}
而不仅仅是一个接口(例如IExecutable,或其他什么…)
而您也有一个处理程序(例如IExecutionHandler,或其他什么…)
如果需要,您仍然可以将其拆分为ICommandExecutionHandler和IQueryExecutionHandler

更新:一次尝试

接下来的代码只是我如何看待它的一个示例。有可能我完全错了。。。因此,请分享你的担忧/我的缺点。我只是想了解这一点

public interface IExecutable { }

public interface ICommand : IExecutable { }

public interface IReturnCommand<TOutput>: ICommand
{
    TOutput Result { get; set; }
}

public interface IQuery<TOutput>: IExecutable
{
    TOutput Result { get; set; }
}

public interface IExecutionHandler<in T>: IDisposable where T : IExecutable
{
    void Execute(T executable);
}

public class CreateAttachments : IReturnCommand<List<Guid>>
{
    public List<Attachment> Attachments { get; set; }

    public List<Guid> Result { get; set; }    
}

public abstract class BaseExecutionHandler : IDisposable
{
    protected readonly IUnitOfWork UnitOfWork;
    private bool _disposed;

    protected BaseExecutionHandler(IUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                UnitOfWork.Dispose();
            }
        }
        _disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

public class AttachmentCommandHandler : BaseExecutionHandler,
    IExecutionHandler<CreateAttachments>
{
    public AttachmentCommandHandler(IUnitOfWork unitOfWork) : base(unitOfWork)
    {
    }

    public void Execute(CreateAttachments command)
    {
        command.Result =  command.Attachments.Select(x => UnitOfWork.Create(x)).ToList();
    }
}

public interface IProcessor : IDisposable
{
    void Process<TExecutable>(TExecutable command) where TExecutable : IExecutable;
}

public class Processor : IProcessor
{
    private readonly Dictionary<IExecutable, IExecutionHandler<IExecutable>> _handlers;
    private readonly IUnitOfWork _unitOfWork;
    private bool _disposed;

    public Processor(IUnitOfWork unitOfWork)
    {
        _handlers = new Dictionary<IExecutable, IExecutionHandler<IExecutable>>();
        _unitOfWork = unitOfWork;
    }

    private IExecutionHandler<IExecutable> GetHandler<TExecutable>(TExecutable executable) where TExecutable: IExecutable
    {
        if (_handlers.ContainsKey(executable))
        {
            return _handlers[executable];
        }
        var handlerType = typeof(IExecutionHandler<>).MakeGenericType(executable.GetType());
        var handler = Activator.CreateInstance(handlerType, _unitOfWork) as IExecutionHandler<IExecutable>;
        _handlers.Add(executable, handler);
        return handler;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                foreach (var handler in _handlers.Values)
                {
                    handler.Dispose();
                }
            }
        }
        _disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    public void Process<TExecutable>(TExecutable executable) where TExecutable : IExecutable
    {
        var handler = GetHandler(executable);
        handler.Execute(executable);
    }
}

public class AttachmentController : ApiController
{
    private readonly IProcessor _processor;

    public AttachmentController(IProcessor processor)
    {
        _processor = processor;
    }

    public List<Guid> Post(List<Attachment> attachments)
    {
        var command = new CreateAttachments { Attachments = attachments };
        _processor.Process(command);
        return command.Result;
    }

    [EnableQuery]
    public IQueryable<Attachment> Get()
    {
        var query = new GetAllAttachments { };
        _processor.Process(query);
        return query.Result;
    }

    protected override void Dispose(bool disposing)
    {
        _processor.Dispose();
        base.Dispose(disposing);
    }
}
公共接口可执行{}
公共接口ICommand:IExecutable{}
公共接口命令:ICommand
{
TOutput结果{get;set;}
}
公共接口IQuery:可执行
{
TOutput结果{get;set;}
}
公共接口IExecutionHandler:IDisposable其中T:IExecutable
{
无效执行(T可执行);
}
公共类CreateAttachments:IReturnCommand
{
公共列表附件{get;set;}
公共列表结果{get;set;}
}
公共抽象类BaseExecutionHandler:IDisposable
{
受保护的只读IUnitOfWork;
私人住宅;
受保护的BaseExecutionHandler(IUnitOfWork unitOfWork)
{
UnitOfWork=UnitOfWork;
}
受保护的虚拟void Dispose(bool disposing)
{
如果(!\u已处置)
{
如果(处置)
{
UnitOfWork.Dispose();
}
}
_这是真的;
}
公共空间处置()
{
处置(真实);
总干事(本);
}
}
公共类AttachmentCommandHandler:BaseExecutionHandler,
IExecutionHandler
{
public AttachmentCommandHandler(IUnitOfWork unitOfWork):基础(unitOfWork)
{
}
public void Execute(CreateAttachments命令)
{
command.Result=command.Attachments.Select(x=>UnitOfWork.Create(x)).ToList();
}
}
公共接口IPProcessor:IDisposable
{
void进程(texecuctable命令),其中texecuctable:IExecutable;
}
公共类处理器:IPProcessor
{
专用只读字典处理程序;
私人只读i工作单元(unitof工作单元);;
私人住宅;
公共处理器(IUnitOfWork unitOfWork)
{
_handlers=newdictionary();
_unitOfWork=unitOfWork;
}
私有IExecutionHandler GetHandler(TExecutable可执行文件),其中TExecutable:IExecutable
{
if(_handlers.ContainsKey(可执行))
{
返回_处理程序[可执行];
}
var handlerType=typeof(IExecutionHandler).MakeGenericType(executable.GetType());
var handler=Activator.CreateInstance(handlerType,_unitOfWork)作为IExecutionHandler;
_Add(可执行文件,handler);
返回处理程序;
}
受保护的虚拟void Dispose(bool disposing)
{
如果(!\u已处置)
{
如果(处置)
{
foreach(在_handlers.Values中的变量处理程序)
{
handler.Dispose();
}
}
}
_这是真的;
}
公共空间处置()
{
处置(真实);
总干事(本);
}
public void进程(texecuctable可执行文件),其中texecuctable:IExecutable
{
var handler=GetHandler(可执行文件);
handler.Execute(可执行);
}
}
公共类AttachmentController:ApiController
{
专用只读IPProcessor\u处理器;
公共附件控制器(IPProcessor处理器)
{
_处理器=处理器;
}
公共列表帖子(列表附件)
{
var命令=newcreateattachments{Attachments=Attachments};
_处理程序(命令);
返回命令。结果;
}
[启用查询]
公共IQueryable Get()
{
var query=newgetAllAttachments{};
_处理程序(查询);
返回查询结果;
}
受保护的覆盖无效处置(布尔处置)
{
_processor.Dispose();
基地。处置(处置);
}
}

如果我理解正确,您会混淆这里的首字母缩略词。从你的问题来看,似乎你并不是在问模式,但你可能会问原则

在这种情况下,简言之:

命令 更改系统的状态,但不返回值

询问 返回结果,不改变系统的可观察状态(无副作用)

我将尝试演示通用接口(及其实现)和非通用接口之间的区别。本演示中显示的类似思维方式适用于通用查询处理程序

解决你问题的技术方面 通用命令处理程序接口如下所示:

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}
最后是命令处理程序的一个示例使用者:

public class ExampleCommand
{
    public int Id { get; set; }
    public string Name { get; set; }
}
public class ExampleService
{
    private readonly ICommandHandler<ExampleCommand> commandHandler;

    public ExampleService(ICommandHandler<ExampleCommand> handler)
    {
        commandHandler = handler;
    }

    public void DoStuff(int id, string name)
    {
        var command = new ExampleCommand
        {
            Id = id,
            Name = name
        };

        commandHandler.Handle(command);
    }
}
在本例中,您无法修饰此处理程序,因为它没有实现接口

还值得注意的是,使用此设置,您只需要对命令处理程序进行单元测试,而不需要对服务的
DoStuff()
方法进行单元测试,因为行为在命令处理程序中

关于CQR的说明 图中的CQR与像CQS这样的OOP方法在技术上有所不同

我想理解为什么应该使用两个接口,而不是一个接口

如果查询和命令具有不同的行为契约,则应该使用两个接口

因此,解决这个问题的方法是开始考虑签名在每个接口中的声明,以及共同点是否真的意味着相同的事情。 命令和查询都是不可变的;如果你仔细考虑一下,你就会意识到你真的不希望编码到命令或查询中的状态在飞行中被修改。因此,在CQS意义上,接口中的函数都应该是查询
public class ExampleService
{
    private readonly ICommandHandler<ExampleCommand> commandHandler;

    public ExampleService(ICommandHandler<ExampleCommand> handler)
    {
        commandHandler = handler;
    }

    public void DoStuff(int id, string name)
    {
        var command = new ExampleCommand
        {
            Id = id,
            Name = name
        };

        commandHandler.Handle(command);
    }
}
public ExampleService(ExampleCommandHandler handler)
public interface IMessage {...}
public interface ICommand : IMessage {...}
public interface IQuery : IMessage {...}
public interface CQCommonThing : IMessage {...}
public interface ICommand : CQCommonThing {...}
public interface IQuery : CQCommonThing {...}
public interface IQuery{}
public interface ICommand{}
public class AttachmentCommandHandler : BaseExecutionHandler,
    IExecutionHandler<CreateAttachments>
{
    public void Execute(CreateAttachments command)
    {
        command.Result =  command.Attachments.Select(x => UnitOfWork.Create(x)).ToList();
    }
}
public interface IReturnCommand<TOutput>: ICommand
{
    TOutput Result { get; set; }
}
public class CreateAttachments : ICommand
{
    // or a List<Pair<Guid, Attachment> if you prefer
    // or maybe the ID is part of the attachment
    public Map<Guid, Attachment> Attachments { get; set; }
}