Differences

This shows you the differences between two versions of the page.

Link to this comparison view

it:ad:patterns:command_pattern [2019/03/24 12:17] (current)
Line 1: Line 1:
 +# IT:​AD:​Patterns:​Command Pattern #
  
 +<div nav>
 +
 +* [[../​|(UP)]]
 +{{indexmenu>​.#​2|nsort tsort}}
 +
 +* See also:
 +   * [[IT/​AD/​Patterns/​CQRS/​]]
 +   * [[IT/​AD/​Patterns/​OT/​]] <- operational transformation
 +   * http://​www.pluralsight.com/​courses/​aspdotnet-undo
 +       * Not exactly Command Pattern, but close enough.
 +
 +
 +
 +</​div>​
 +
 +## The *Classic* ICommand ##
 +
 +The basic Command pattern is as old as the hills. Characteristics of the *ICommand* contract are:
 +
 +* Execute takes no argument
 +* Execute returns no arguments
 +
 +The rational is that it can be a serializable object representation of a method, containing all the arguments it needs to do its job, traditionally started using the object'​s only method -- `Execute()`:​
 +
 +<sxh csharp>
 +public void interface ICommand ​ {
 +  void Execute();
 +}
 +</​sxh>​
 +
 +
 +
 +
 +
 +Over the years, depending on where its being used, it has been tweeked a bit. A classic variant is the following:
 +
 +<sxh csharp>
 +public void interface IUndoableCommand ​ {
 +  public event CanExecuteChanged;​
 +  bool CanExecute {get;}
 +  bool CanUnexecute {get;}
 +  void Execute();
 +  void Unexecute();​
 +}
 +</​sxh>​
 +
 +
 +Note that you'll sometimes see (namely `Validation`),​ something called a `Command` that has an  `Execute` method that has been stretched into returning a `Result` object of some kind.... But that's really no longer a `Command`, and simply an `IFunc{TReturn}` of some kind.
 +
 +All that aside, an example of an implementation of the classic contract would be something like the following:
 +
 +<sxh csharp>
 +public class LogMessage : ICommand {
 +  ...
 +  public LogMessage(ITracingService tracingService,​ IXFerService xferService,​ decimal amount, Account src, Account target){...}
 +
 +  public void Execute()
 +  {
 +    _xferService.XFer(src,​target,​amount);​
 +    _loggingService.Log("​some message..."​);​
 +  }
 +  ...
 +}
 +</​sxh>​
 +
 +
 +Go a little further, add the Undo part, and you have a safe way to commit, and rollback every and all transactions done. Presto, safety in one move.
 +
 +Note that you don't have to make every operation in your app a Command -- that would be expensive overkill -- so you can skip the UI stuff and just Command up Financial operations, and such.
 +
 +## The CommandMessage Pattern ###
 +
 +### Disco is Over ###
 +The classic `ICommand` pattern is `very` useful...but it also was defined before IoC's became common, and the two concepts conflict.
 +
 +The problem with the above example implementation of the classic `ICommand` pattern – when trying to use it in a [[IT/​AD/​Patterns/​SOLID/​DIP/​]]/​[[IT/​AD/​Patterns/​IoC/​]] ​ structured application – is of course the `constructor`.
 +
 +This is because in the traditional definition of the `Command Pattern`, the `Constructor` is used to pass in the arguments required in order for the `Execute()` method to not need additional arguments when invoked later.
 +
 +As you can imagine, this is not going to work...in fact it plain old mucks up the machinery of most IoC’s which are geared to push in [[IT/​AD/​Patterns/​Service Pattern/]] dependencies,​ not `scalar arguments` (at least without a lot of work).
 +
 +
 +### New Dance Moves ###
 +
 +A set of patterns that comes to the rescue is the `ICommandHandler` + `ICommandMessage` + `ICommandMessageDispatcher` patterns.
 +
 +<sxh csharp>
 +//The Arguments half of the contract:
 +public interface ICommandMessage {}
 +
 +//The Action/​Execute half of the contract:
 +public interface ICommandMessageHandler<​in TCommandMessage,​ out TCommandResponse>​
 + where TCommandMessage : ICommandMessage ​
 + where TCommandResponse : ICommandResponse ​
 + {
 +  //Note: action is separated from command args:
 +  void Execute(TCommandMessage commandMessage);​
 + }
 +
 +//The runner: ​
 +public interface ICommandDispatcher {
 +  void Execute<​TCommandMessage>​(TCommandMessage commandMessage) ​
 +    where TCommandMessage:​ ICommandMessage;​
 +}
 +</​sxh>​
 +
 +
 +Ignoring the `ICommandDispatcher` (sometimes called `ICommandBus`) for a second, let’s look at what’s been defined.
 +
 +What we are doing here is separating the Args required for execution from the Execution part.
 +
 +This means that on one hand, the command message/​data can be serialized, and on the other, the Executor / Handler can be DI’ed with services as required. ​
 +Conflict solved!
 +
 +We can rewrite our log command as something like:
 +
 +<sxh csharp>
 +public class XFerCommandMessage : ICommandMessage{
 +  public Account Src {get;set;}
 +  public Account Target {get;set;}
 +  public decimal Amount {get;set;}
 +}
 +
 +public class XFerMessageHandler : ICommandMessageHandler<​LogCommandMessage>​{
 +  IXFerService _xferService;​
 +
 +  public LogCommandMessageHandler(IXFerService xferService){
 +    _xferService = xferService;​
 +  }
 +
 +  public void Execute(XFerCommandMessage xferCommandMessage){ ​   _xferService.Xfer(xferCommandMessage.Src,​xferCommandMessage.Target,​xferComandMessage.Amount);​
 +  }
 +}
 +</​sxh>​
 +
 +
 +This will be run using:
 +
 +<sxh csharp>
 +#!c#
 +public interface ICommandDispatcher {
 +  TCommandResponse Execute<​TCommandMessage,​TComandResponse>​(TCommandMessage comandMessage)
 +   where TCommandMessage:​ ICommandMessage
 +   where TCommandResponse:​ ICommandResponse
 +}
 +public class CommandDispatcher : ICommandDispatcher {
 +  public TCommandResult Execute<​TCommandMessage>​(TCommandMessage commandMessage) ​
 +    where TCommandMessage : ICommandMessage
 +    where TCommandResponse:​ ICommandResponse
 +    {    ​
 +
 +    //Let the bus find the Handler appropriate
 +    //for the given command:
 +    ICommandMessageHandler<​TCommandMessage>​ commandMessageHandler = 
 +    _serviceLocator.Current.GetService<​ICommandMessageHandler<​TCommandMessage>>​();​
 +
 +    //and execute it (no return):
 +    commandMessageHandler.Execute(command);​
 +  }
 +}
 +</​sxh>​
 +
 +
 +Et voila. Now you can do something as simple as first:
 +
 +<sxh csharp>
 + ​XFerMessage xferMessage= new XFerMessage(src,​target,​100.00);​
 + ​CommandMessageDispatcher.Execute(xferMessage);​
 + //​Bam! The dispatcher finds the EventHandler registered
 + //in the IoC that will work with the above message,
 + //​invokes it Execute method, passing it the Message,
 + //and bob's (Bob Martin...)'​s your uncle. ​
 +
 +</​sxh>​
 +
 +Again, take that further and the `ServiceBus`/​`ServiceDispatcher` gets an `Undo` method as well.
 +
 +Take it even further -- add `Memento` to the solution, and you have a serializeable queue of undoable operations.
 +
 +
 +## Addendums ##
 +
 +PPS: About nomenclature:​ Note that many developers call the above `ICommandMessage` ​ `ICommandData` – or worse `ICommand`. I think both of these names are inappropriate, ​
 +
 +For one the word `Command` implies execution – which an argument package obviously is not. I don’t have anything against Data…it is appropriate,​ but I prefer `Message`, as it implies the ability to cross layers and tiers, and be executed where ever the Handler is located. ​
 +
 +Just seems more…future proof. ​ But don’t be surprised if in many cases you see it called ICommand, that’s all.
 +
 +PPPS: Finally, note that all the above contracts etc are all already defined in [[Projects/​XActLib/​]]'​s `XAct.Core` assembly -- don't go reinventing the wheel.
 +
 +
 +
 +
 +## Resources ##
 +* [[http///​bit.ly/​vDbTkt/​]]
 +
 +
 +-----
 +
 +## Summary ##
 +
 +# IT:​AD:​Patterns:​Command #
 +
 +
 +
 +<callout type="​Navigation"​ class="​small">​
 +* [[../​|(UP)]]
 +{{indexmenu>​.#​2|nsort tsort}}
 +
 +
 +
 +
 +---
 +
 +
 +</​callout>​
 +
 +
 +## Synopsis ##
 +
 +As per this [post](http://​bit.ly/​vDbTkt):​
 +
 +    public interface ICommandResponse {
 +      bool Success {get;}
 +      bool Message {get;}
 +      IEnumerable<​Exception>​ Exceptions {get;}
 +    }
 +    public interface ICommandMessage {
 +    }
 +    public interface ICommandMessageHandler<​in TCommandMessage>​
 +     where TCommandMessage : ICommandMessage {
 +      //Note: action is separated from command args:
 +      ICommandResult Execute(TCommandMessage commandMessage);​
 +    }
 +    public interface ICommandDispatcher {
 +      ICommandResult Execute<​TCommandMessage>​(TCommandMessage commandMessage) ​
 +        where TCommandMessage:​ ICommandMessage;​
 +      //​IEnumerable<​Exception>​ Validate<​TCommandMessage>​(TCommandMessage commandMessage) ​
 +      //where TCommandMessage : ICommandMessage;​
 +    }