Skip to content
This repository has been archived by the owner on Jul 28, 2023. It is now read-only.

Commands

Jon Sequeira edited this page Jan 10, 2015 · 6 revisions

Commands

Every transition in a process modeled using an aggregate starts with a command being sent to an aggregate. The command models the intent for the change:

	var withdrawFunds = new WithdrawFunds {
		Amount = 10.00m
	};

	account.Apply(withdrawFunds);

Its.Cqrs uses command classes (deriving from ICommand<T> or Command<T>) instead of methods to expose ways to change the aggregate. This allows a more robust model for authorization, validation, and serializability.

Command validation

The primary responsibility of a command class is to perform validation checks, including validation of business rules. When a command is applied to an aggregate, a number of checks are performed:

  • Idemptotency: Has this exact command instance been applied to the aggregate in the past?

  • Authorization: Is the sender of the command authorized, as defined by the domain, to perform this command?

  • Applicability to the current version of the aggregate: By setting Command<T>.AppliesToVersion a command can be pre-emptively invalidated by changes to the aggregate that may happen before the command is applied. (This is separate from the usual concurrency control provided by the event store, and is mostly used when scheduling commands for future delivery.)

  • Self-validation: Command<T>.CommandValidator provides a way for a command class to expose validation of its own state, analagous to input or parameter validation. This check is generally performed before an aggregate is sourced from the event store. Here's an example from Sample.Banking.Domain.WithdrawFunds:

public override IValidationRule CommandValidator
{
    get
    {
        return Validate.That<WithdrawFunds>(cmd => cmd.Amount > 0)
                       .WithErrorMessage("You cannot make a withdrawal for a negative amount.");
    }
}
  • Validation against the aggregate: ICommand<T>.Validator provides a second validator that allows the command to be validated against the state of the aggregate once it has been sourced from the event store. Here's an example:
public override IValidationRule<CheckingAccount> Validator
{
    get
    {
        var accountIsNotClosed =
            Validate.That<CheckingAccount>(account => account.DateClosed == null)
                    .WithErrorMessage("You cannot make a withdrawal from a closed account.");

        var fundsAreAvailable = Validate.That<CheckingAccount>(account => account.Balance >= Amount);

        return new ValidationPlan<CheckingAccount>
        {
            accountIsNotClosed,
            fundsAreAvailable.When(accountIsNotClosed)
        };
    }
}

It's worth noting that these validators are exposed as properties so that you can do a validation check without actually applying a command, for example to provide feedback via a UI or API about what actions are allowed or what parameters are incorrect:

var validationReport = account.Validate(withdrawFunds);

The ValidationReport allows you to see details about all of the failed validation rules and use that information as needed.

When applying the command, however, validation failures result in a CommandValidationException, which contains a ValidationReport.

(For more information regarding the validation library used in these examples, have a look at Its.Validation.)

Clone this wiki locally