Skip to content
Aaron Hanusa edited this page Nov 10, 2021 · 82 revisions

peasy

The simplest possible example

Start by creating a new project of your chosing and install the peasy nuget package.

Next create a domain object (DTO) that implements IDomainObject<T>:

using Peasy;

public class Person : IDomainObject<int>
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string City { get; set; }
    public int Age { get; set; }
}

Then create a data proxy (aka repository) that implements IDataProxy<T, TKey> (most methods not implemented for brevity):

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Peasy;

namespace samples
{
    public class PersonStubDataProxy : IDataProxy<Person, int>
    {
        public Task<IEnumerable<Person>> GetAllAsync()
        {
            return Task.FromResult<IEnumerable<Person>>
            (
                new []
                {
                    new Person() { ID = 1, Name = "Jimi Hendrix" },
                    new Person() { ID = 2, Name = "James Page" },
                    new Person() { ID = 3, Name = "David Gilmour" }
                }
            );
        }

        public Task<Person> InsertAsync(Person entity)
        {
            return Task.FromResult(new Person() { ID = new Random(300).Next(), Name = entity.Name });
        }

        public Task DeleteAsync(int id)
        {
            throw new NotImplementedException();
        }

        public Task<Person> GetByIDAsync(int id)
        {
            throw new NotImplementedException();
        }

        public Task<Person> UpdateAsync(Person entity)
        {
            throw new NotImplementedException();
        }
    }
}

Finally, create a service class, which exposes CRUD commands responsible for subjecting IDataProxy invocations to business rules before execution:

using Peasy;

public class PersonService : ServiceBase<Person, int>
{
    public PersonService(IDataProxy<Person, int> dataProxy) : base(dataProxy)
    {
    }
}

Now let's consume our PersonService:

var service = new PersonService(new PersonStubDataProxy());
var getResult = await service.GetAllCommand().ExecuteAsync();

if (getResult.Success)
{
    foreach (var person in getResult.Value)
        Debug.WriteLine(person.Name);  // prints each person's name retrieved from PersonMockDataProxy.GetAll
}

var newPerson = new Person() { Name = "Erlich Bachman", City = "Madison", Age = 32 };
var insertResult = await service.InsertCommand(newPerson).ExecuteAsync();

if (insertResult.Success)
{
    Debug.WriteLine(insertResult.Value.ID.ToString()); // prints the id value assigned via PersonMockDataProxy.Insert
}

Let's create a business rule whose execution must be successful before the call to IDataProxy.InsertAsync is invoked

using System.Threading.Tasks;
using Peasy;

public class ValidAgeRule : RuleBase
{
    private int _age;

    public ValidAgeRule(int age)
    {
        _age = age;
    }

    protected override Task OnValidateAsync()
    {
        return IfNot(() => _age >= 21)
            .ThenInvalidateWith("You are not old enough");

        // You can use this syntax too if you prefer:
        // if (_age < 21)
        // {
        //     Invalidate("You are not old enough");
        // }
        // return Task.CompletedTask;
    }
}

And wire it up in our PersonService to ensure that it gets fired before inserts:

using System.Collections.Generic;
using System.Threading.Tasks;
using Peasy;
using Peasy.Extensions;

public class PersonService : ServiceBase<Person, int>
{
    public PersonService(IDataProxy<Person, int> dataProxy) : base(dataProxy)
    {
    }

    protected override Task<IEnumerable<IRule>> OnInsertCommandGetRulesAsync(Person resource, ExecutionContext<Person> context)
    {
        return TheseRules(new ValidAgeRule(resource.Age));

        // You can use this syntax too if you prefer:
        // return Task.FromResult(new ValidAgeRule(resource.Age).ToArray());
    }
}

Testing it out:

var service = new PersonService(new PersonStubDataProxy());
var newPerson = new Person() { Name = "Erlich Bachman", City = "Madison", Age = 18 };
var insertResult = await service.InsertCommand(newPerson).ExecuteAsync();

if (insertResult.Success)
{
    Debug.WriteLine(insertResult.Value.ID.ToString());
}
else
{
    // This line will execute and print 'You are not old enough'
    // Note that insertResult.Value will be NULL as PersonStubDataProxy.Insert did not execute due to failed rule
    Debug.WriteLine(insertResult.Errors.First());
}

Let's create one more rule, just for fun:

using System.Threading.Tasks;
using Peasy;

public class ValidCityRule : RuleBase
{
    private string _city;

    public ValidCityRule(string city)
    {
        _city = city;
    }

    protected override Task OnValidateAsync()
    {
        return If(() => _city == "Nowhere")
            .ThenInvalidateWith("Nowhere is not a city");

        // You can use this syntax too if you prefer:
        // if (_city == "Nowhere")
        // {
        //     Invalidate("Nowhere is not a city");
        // }
        // return Task.CompletedTask;
    }
}

We'll associate this one with inserts too:

using System.Collections.Generic;
using System.Threading.Tasks;
using Peasy;

public class PersonService : ServiceBase<Person, int>
{
    public PersonService(IDataProxy<Person, int> dataProxy) : base(dataProxy)
    {
    }

    protected override Task<IEnumerable<IRule>> OnInsertCommandGetRulesAsync(Person resource, ExecutionContext<Person> context)
    {
        return TheseRules
        (
            new ValidAgeRule(resource.Age),
            new ValidCityRule(resource.City)
        );

        // You can use syntax like this too if you prefer:
        // return Task.FromResult<IEnumerable<IRule>>(new IRule[]
        // {
        //     new ValidAgeRule(resource.Age),
        //     new ValidCityRule(resource.City)
        // });
    }
}

And test it out:

var service = new PersonService(new PersonStubDataProxy());
var newPerson = new Person() { Name = "Erlich Bachman", City = "Nowhere", Age = 18 };
var insertResult = await service.InsertCommand(newPerson).ExecuteAsync();

if (insertResult.Success)
{
    Debug.WriteLine(insertResult.Value.ID.ToString());
}
else
{
    // This line will execute and print 'You are not old enough' and 'Nowhere is not a city'
    // Note that insertResult.Value will be NULL as PersonStubDataProxy.Insert did not execute due to failed rule
    foreach (var error in insertResult.Errors)
        Debug.WriteLine(error);
}

Finally, let's supply valid data and watch it be a success

var service = new PersonService(new PersonStubDataProxy());
var newPerson = new Person() { Name = "Erlich Bachman", City = "Madison", Age = 21 };
var insertResult = await service.InsertCommand(newPerson).ExecuteAsync();

if (insertResult.Success)
{
    Debug.WriteLine(insertResult.Value.ID.ToString()); // prints the id value assigned via PersonStubDataProxy.Insert
}
else
{
    foreach (var error in insertResult.Errors)
        Debug.WriteLine(error);
}