问题
I'm new at the CQRS pattern, but I'd like to understand why you should use two interfaces:
public interface IQuery{}
public interface ICommand{}
Instead of just one interface (for example IExecutable, or whatever...)
Than you also have one handler (for example IExecutionHandler, or whatever...)
And if you want, you could still split it up into an ICommandExecutionHandler and a IQueryExecutionHandler
Update: an attempt
Next code is just an example of how I see it. It's possible that I'm completely wrong with this... so please share your concerns/my faults. I'm just trying to understand this.
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);
}
}
回答1:
I'd like to understand why you should use two interfaces, instead of just one interface
You should use two interfaces if Queries and Commands have different behavior contracts.
So the way to flesh out this question is to start to consider what signatures would be declared in each interface, and whether or not the common ones really mean the same thing.
Commands and Queries are both immutable; if you give it a bit of thought, you'll realize you really don't want the state encoded into the command or query to be modified in flight. So the functions in the interface should all be queries, in the CQS sense - functions that return a copy of the state of the object without changing it in any way.
Given that, what do commands and queries have in common? Maybe a bunch of meta data, so that the right kinds of handlers get invoked, so that you can correlate the responses with the requests, and so on. The abstraction of all of this is a Message (see Enterprise Integration Patterns, Gregor Hohpe).
So you can certainly justify
public interface IMessage {...}
and so you might have
public interface ICommand : IMessage {...}
public interface IQuery : IMessage {...}
Depending upon whether or not there are queries which are common to all Commands that are not common to all Messages. It's possible that your implementation might even want
public interface CQCommonThing : IMessage {...}
public interface ICommand : CQCommonThing {...}
public interface IQuery : CQCommonThing {...}
But I'm stumped to come up with any examples of queries that would belong in common to Queries and Commands that don't also belong to Message.
On the other hand, if you are considering marker interfaces, where you aren't actually specifying a contract, like so:
public interface IQuery{}
public interface ICommand{}
then I don't know of any reason that you would want to combine these, except in the sense that you might want to use IMessage
instead.
Reviewing your implementation, it looks like you lost the plot somewhere.
public class AttachmentCommandHandler : BaseExecutionHandler,
IExecutionHandler<CreateAttachments>
{
public void Execute(CreateAttachments command)
{
command.Result = command.Attachments.Select(x => UnitOfWork.Create(x)).ToList();
}
}
Is this a command "create a bunch of entities in my system of record", or a query "return to me a list of created entities"? Trying to do both at the same time violates CQS, which is a hint that you are on the wrong track.
In other words, this construct here
public interface IReturnCommand<TOutput>: ICommand
{
TOutput Result { get; set; }
}
is bizarre -- why would you ever need such a thing when using the CQRS pattern?
Using CreateAttachments as an example, your current implementation calls for the client sending to the command handler, and receiving a list of matching guids in return. That's challenging to implement -- but you don't have to choose to do it that way. What's wrong with generating the IDs on the client, and making them part of the command? Do you think client generated GUIDs are somehow less unique than a server generated GUID?
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; }
}
"Look, Ma, no Result." The caller just needs an acknowledgement of the command (so that it can stop sending it); and then it can synchronize via query.
回答2:
If I get it correctly you are confusing acronyms here. From your question to me it seems that you are not really asking about Command and Query Responsibility Segregation pattern, but you might ask about the Command-Query Separation principle.
And in this case the basics in short are that:
Commands
Change the state of a system but do not return a value
Queries
Return a result and do not change the observable state of the system (are free of side effects).
I will try to demonstrate the difference between having a generic interface (and its implementation) and a non-generic interface. A similar way of thinking shown in this demonstration applies to a generic Query Handler.
Addressing the technological side of your question
A generic command handler interface as:
public interface ICommandHandler<TCommand>
{
void Handle(TCommand command);
}
Its sample implementation:
public class ExampleCommandHandler : ICommandHandler<ExampleCommand>
{
public void Handle(ExampleCommand command)
{
// Do whatever logic needed inside this command handler
}
}
Example Command
that you pass into the command handler:
public class ExampleCommand
{
public int Id { get; set; }
public string Name { get; set; }
}
And finally an example consumer of the Command handler:
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);
}
}
Benefits of using a generic ICommandHandler
Using the generic command handler lets users depend on this abstraction rather than exactly implemented command handlers.
If you would depend the exact implementation of this ExampleCommandHandler
that won't implement the generic interface, the example service's constructor would have a dependency like:
public ExampleService(ExampleCommandHandler handler)
In this instance, you couldn't decorate this handler because it doesn't implement an interface.
Also worth noting that with this setup you only need to unit test the Command handler and not the service's DoStuff()
method, since the behaviour is in the Command handler.
Note about CQRS
CQRS in this picture is a technical difference than an OOP method like CQS.
来源:https://stackoverflow.com/questions/36428850/cqrs-pattern-interfaces