Skip to content
Aaron Hanusa edited this page Feb 8, 2021 · 7 revisions

When a command is invoked, its parameters can be subjected to business and validation rules. Business rules are generally considered to be rules that govern changing the state of data, or DTOs within the context of the Peasy framework.

Business rules cover a wide array of use cases. For example, you may want a business rule that states that new customers have to be at least 18 years old, or you may want to ensure that an item can't be deleted from a data store if it has been shipped.

You may also want authorization rules to allow or prohibit access to particular service commands. For example, administrators might be the only users that can insert or update product information.

The business rules engine is at the heart of the Peasy framework, and offers an easy way to create rules that are flexible, easy to consume, configure, reuse, maintain, and test, all through the usage of RuleBase.

Public methods

ExecuteAsync

Asynchronously executes the rule and sets the IsValid status based on success or failure of the rule execution. If IsValid is false, the ErrorMessage property will contain the error that describes how the validation failed.

Creating a business rule

To create a business rule, simply:

  • Create a class that inherits from RuleBase.
  • Override OnValidateAsync.
  • Invoke Invalidate if the rule fails validation.
public class CustomerAgeVerificationRule : RuleBase
{
    private DateTime _birthDate;

    public CustomerAgeVerificationRule(DateTime birthDate)
    {
        _birthDate = birthDate;
    }

    protected override Task OnValidateAsync()
    {
        if ((DateTime.Now.Year - _birthDate.Year) < 18)
        {
            Invalidate("New users must be at least 18 years of age");
        }
        return Task.CompletedTask;
    }
}

The CustomerAgeVerificationRule simply checks that the supplied birth date is at least 18 years of age.

Here's an example writing the same rule using an alternate supported syntax:

public class CustomerAgeVerificationRule : RuleBase
{
    private DateTime _birthDate;

    public CustomerAgeVerificationRule(DateTime birthDate)
    {
        _birthDate = birthDate;
    }

    protected override Task OnValidateAsync()
    {
        return If(() => (DateTime.Now.Year - _birthDate.Year) < 18)
            .ThenInvalidateWith("New users must be at least 18 years of age");
    }
}

Retrieving data from a rule

Rules can be passed any form of data from any data source imaginable. Often times, you'll want the rule itself to be responsible for obtaining data that it will use to determine validity.

Here's an example:

public class CanDeleteOrderItemRule : RuleBase
{
    private int _itemId;
    private IDataProxy<OrderItem, int> _orderItemDataProxy;

    public CanDeleteOrderItemRule(int itemId, IDataProxy<OrderItem, int> orderItemDataProxy)
    {
        _itemId = itemId;
        _orderItemDataProxy = orderItemDataProxy;
    }

    protected override async Task OnValidateAsync()
    {
        var orderItem = await _orderItemDataProxy.GetByIDAsync(_itemId);
        if (orderItem.Status == STATUS.Shipped)
        {
            Invalidate("This item has been shipped and cannot be deleted");
        }
    }
}

Testing rules

Here is how we can easily test the CustomerAgeVerificationRule:

var invalidRule = await new CustomerAgeVerificationRule(DateTime.Now.AddYears(-17)).ExecuteAsync();
invalidRule.IsValid.ShouldBe(false);
invalidRule.ErrorMessage.ShouldBe("New users must be at least 18 years of age");

var validRule = await new CustomerAgeVerificationRule(DateTime.Now.AddYears(-30)).ExecuteAsync();
validRule.IsValid.ShouldBe(true);
validRule.ErrorMessage.ShouldBeNull();