Skip to content

1.3 Distributed process

Marjan Nikolovski edited this page Apr 9, 2021 · 1 revision

Purpose

We use distributed process when we want to expose an endpoint to be consumed from a client, but a part of the logic should be executed in a background service. The endpoint path is generated by a convention of folder structure. For example if we have a process in our “Web” project with full class name Web.Processes.AddUser the path will be /api/Processes/AddUser

The distributed process contains 5 layers:

  • Auth
  • Validate
  • Handle
  • Map
  • Work

A communication channel aspect must be configured on both web and background application for this distributed process to work.

How to use

public class MyRequestData : IDtoData
{
    public string Input1 { get; set; }
    public int Input2 { get; set; }

    /// <summary>
    /// Sanitize
    /// </summary>
    /// <param name="sanitizer"></param>
    public void Sanitize(HtmlSanitizer sanitizer)
    {
        Input1 = sanitizer.Sanitize(Input1);
    }
}

public class MyTransientData : ITransientData
{
    public string Input1 { get; set; }
    public int Input2 { get; set; }
}

[ApiProcess(HttpMethod = ApiProcessMethod.POST)]
public class MyDistributedProcess : DistributedProcess<MyTransientData, MyRequestData, VoidResult>
{
    /// <summary>
    /// Auth process
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public override VoidResult Auth(MyRequestData request)
    {
        return Ok();
    }

    /// <summary>
    /// Validate process
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public override VoidResult Validate(MyRequestData request)
    {
        return Ok();
    }

    /// <summary>
    /// Handle process
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public override VoidResult Handle(MyRequestData request)
    {
        return Ok();
    }

    /// <summary>
    /// Map process
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public override MyTransientData Map(MyRequestData request, MethodResult<int> result)
    {
        return new MyTransientData
        {
            Input1 = request.Input1,
            Input2 = request.Input2
        };
    }

    /// <summary>
    /// Background work process
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public override VoidResult Work(MyTransientData user)
    {
        return Ok();
    }
}

Configuration

  • 1 or 2 in generic arguments
  • VoidResult, MethodResult or ListResult out generic argument
  • SignalsAuthorizeProcess and Critical attributes
  • Validation with specification pattern
  • ApiProcess attribute
  • Context.HttpContext property with all http context information

Examples

public class UserDtoData : IDtoData, ITransientData
{
    public string Email { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }

    /// <summary>
    /// Sanitize
    /// </summary>
    /// <param name="sanitizer"></param>
    public void Sanitize(HtmlSanitizer sanitizer)
    {
        Email = sanitizer.Sanitize(Email);
        Username = sanitizer.Sanitize(Username);
        Password = sanitizer.Sanitize(Password);
    }
}

[ApiProcess(HttpMethod = ApiProcessMethod.POST)]
public class RegisterUser : DistributedProcess<UserDtoData, UserDtoData, MethodResult<int>>
{
    [Import] private IUserRepository UserRepository { get; set; }

    /// <summary>
    /// Auth process
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public override MethodResult<int> Auth(UserDtoData user)
    {
        return Ok();
    }

    /// <summary>
    /// Validate process
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public override MethodResult<int> Validate(UserDtoData user)
    {
        return BeginValidation()
            .Validate(new UserHasEmailAndPassword(), user)
            .ReturnResult();
    }

    /// <summary>
    /// Handle process
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public override MethodResult<int> Handle(UserDtoData user)
    {
        var userId = UserRepository.Register(user);
        return userId;
    }

    /// <summary>
    /// Map process
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public override UserDtoData Map(UserDtoData request, MethodResult<int> result)
    {
        return request;
    }

    /// <summary>
    /// Background work process
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public override VoidResult Work(UserDtoData user)
    {
        return Continue<SendEmail>().With(user.Email);
    }
}
Clone this wiki locally