Skip to content
ahanusa edited this page Oct 15, 2015 · 35 revisions

The ServiceCommand is the actor responsible for orchestrating initialization logic, validation/business rule execution, and data proxy invocation. ServiceCommand inherits from Command and implements ICommand, and instances of it are returned from the public methods of ServiceBase.

ServiceCommand is meant to serve as an easy way to deliver custom command functionality from your service implementations without the hassle of having to create your own command implementations. However, if you don't find ServiceCommand to be flexible enough, you can also create your own commands that inherit from Peasy.Core.Command or alternatively, implement Peasy.Core.ICommand;

ServiceCommand uses a functional design pattern that wraps functions Func and actions Action that participate in the command execution pipeline. This pattern was used to offer a quick and easy way to expose command methods from your service classes.

ServiceCommand exposes many contructor overloads, each requiring different variations of arguments of type Func and Action covering different use cases. For example, many times you will not need initialization logic or business rules to execute before executing command logic and interacting with data proxies. Other times, you may want to inject initialization logic into the command execution pipeline, but forego business rule validation. Below are a couple of examples:

A simple example:

public ICommand<InventoryItem> GetByProductCommand(long productID)
{
    var proxy = DataProxy as IInventoryItemDataProxy;
    return new ServiceCommand<InventoryItem>
    (
        executeMethod: () => proxy.GetByProduct(productID),
        executeAsyncMethod: () => proxy.GetByProductAsync(productID)
    );
}

In this example, we expose the GetByProductCommand method that returns ICommand<InventoryItem>. Within this method, we instantiate a new ServiceCommand (ServiceCommand<InventoryItem>) passing in functions for the command execution methods. These functions simply interact with its data proxy to retrieve an inventory item.

Note that in this example, we chose the constructor overload that only accepts execution methods, as we don't require any initialization logic or validation of any validation/business rules in our execution pipeline. Also note that we include the parameter names with the constructor args for better readability and that this practice is optional.

A busier example

Here is an example that specifies the logic to execute during command execution and injects business rules into the command execution pipeline offering both synchronous and asynchronous support:

public ICommand<OrderItem> SubmitCommand(long orderItemID)
{
    var proxy = DataProxy as IOrderItemDataProxy;
    return new ServiceCommand<OrderItem>
    (
        executeMethod: () => proxy.Submit(orderItemID, DateTime.Now),
        executeAsyncMethod: () => proxy.SubmitAsync(orderItemID, DateTime.Now),
        getBusinessRulesMethod: () => GetBusinessRulesForSubmit(orderItemID),
        getBusinessRulesAsyncMethod: () => GetBusinessRulesForSubmitAsync(orderItemID)
    );
}

private IEnumerable<IRule> GetBusinessRulesForSubmit(long orderItemID)
{
    var orderItem = DataProxy.GetByID(orderItemID);
    yield return new CanSubmitOrderItemRule(orderItem);
}

private async Task<IEnumerable<IRule>> GetBusinessRulesForSubmitAsync(long orderItemID)
{
    var orderItem = await DataProxy.GetByIDAsync(orderItemID);
    return new[] { new CanSubmitOrderItemRule(orderItem) };
}

In this example, we create an instance of ServiceCommand, but this time we specify business rule methods in addition to our execution methods.

How do I test these methods?

Clone this wiki locally