Pull to refresh

Первый взгляд на Pipelines.NET

Pipelines.NET это C# библиотека, цель которой сделать код читабельнее, проще для тестирования и следовать принципам SOLID. Любой логический процесс может быть разделён на шаги, и эта библиотека даёт пользователю инструменты для разделения логики на шаги, которые в последующем могут быть объединены.


Основные понятия:


  • Логический шаг — это объект Processor;
  • Данные хранятся в объекте Context и из него же они и читаются;
  • Бизнес процесс описывается объектом Pipeline;
  • Описанный pipeline исполняется объектом Runner;
  • Прерванный pipeline не будет выполняться с момента прерывания;
  • Свойства, которые хранятся в объекте context регистро-независимые.

Рассмотрим простой пример:
Есть форма для регистрации гостей на событие. Каждый гость должен быть представлен именем, фамилией и возрастом. Сервер должен проверить данные и добавить модель составленную из данных гостя в список в случае если данные прошли проверку.


Проектируя pipeline я бы начал с описания бизнес-процесса простыми словами, содержащими минимум необходимой информации:


  • Проверить данные гостя;
  • Если данные подходят создать модель из данных;
  • Если модель создана, добавить её в список.

В этой статье, чтобы упростить пример, я буду использовать заранее подготовленный список гостей:


class Program
{
    public static object[] Guests = new [] {
        new {
            Name = "Майк",
            LastName = "Глебов",
            Age = 22,
            Email = "test@sample.net"
        },
        new {
            Name = "Анастасия",
            LastName = "Вермуцкая",
            Age = 24,
            Email = "test@sample.net"
        },
        new {
            Name = "Александр",
            LastName = "Абрамов",
            Age = 15,
            Email = "test@sample.net"
        }
    };
    // ...
}

Проектирование объекта pipeline в библиотеке Pipelines.Net должно быть таким же простым, как и описание бизнесс-процесса. Перед тем как начать реализацию логики я бы рекомендовал сесть и хорошо обдумать бизнесс-процесс, обдумать каждую деталь: проверку данных, предоставление сообщений по мере выполнения процесса, и т.д. даже если это небольшая логика как загрузка строки из интернета, потому что в будущем может понадобиться расширить эту логику, и таким образом сможет получиться хороший pipeline объект. Посмотрим на пример проектирования объекта pipeline:


public static void Main()
{
    // Проектирование объекта pipeline из процессоров, которые будут реализованы позже.
    var guestRegistration = 
        PredefinedPipeline.FromProcessors<
            ValidateGuestData, // Проверить данные гостя;
            CreateGuestModel,  // Если данные подходят создать модель из данных;
            AddGuestToList     // Если модель создана, добавить её в список.
        >();

    // Выполнить регистрацию гостей при помощи описанного раннее объекта pipeline.
    foreach (var guest in Guests) 
    {
        var task = new PipelineContext(guest)
            .RunWithPipeline(guestRegistration);
    }

    // Список гостей должен содержать:
    // - Майк Глебов (22)
    // - Анастасия Вермуцкая (24)
    // - Александр Абрамов (15)
}

Быстро создадим простые модели:


/// <summary>
/// Пример объекта модели описывающей гостя. Содержит только поле идентификации.
/// </summary>
public class GuestModel
{
    /// <summary>
    /// Простая строка идентифицирующая гостя.
    /// </summary>
    public string Identification { get; set; }
}

/// <summary>
/// Объект содержащий список гостей.
/// </summary>
public class GuestList
{
    /// <summary>
    /// Список всех зарегестрированных гостей.
    /// </summary>
    public static readonly List<GuestModel> Guests 
        = new List<GuestModel>();
}

Пришло время почувствовать все прелести программной инженерии и реализовать шаги бизнес-процесса:


/// <summary>
/// Процессор выполняющий проверку данных. Здесь должна быть реализована
/// комплексная проверка данных гостя, перед тем, как он будет добавлен в список.
/// </summary>
public class ValidateGuestData : SafeProcessor
{
    public override Task SafeExecute(PipelineContext args)
    {
        // Здесь происходит проверка всех данных. В случае, если какое-то свойство
        // не пройдёт проверку, текущий процесс должен быть прерван с указанием
        // сообщения об ошибке.

        args.IfHasNoProperty("name",
            context => context.AbortPipelineWithErrorMessage(
                "Необходимо указать имя гостя."));

        args.IfHasNoProperty("LastName",
            context => context.AbortPipelineWithErrorMessage(
                "Фамилия необходима для его идентификации."));

        var age = args.GetPropertyValueOrDefault("age", 0);
        if (age < 15)
        {
            args.AbortPipelineWithErrorMessage(
                "Гость должен быть старше 15 лет.");
        }

        return Done;
    }
}

/// <summary>
/// Когда данные пользователя проверены, должна быть составлена модель.
/// </summary>
public class CreateGuestModel : SafeProcessor
{
    public override Task SafeExecute(PipelineContext args)
    {
        // Соединяем все данные пользователя в объект модели.

        var firstName = args.GetPropertyValueOrDefault("name", "[не указано]");
        var lastName = args.GetPropertyValueOrDefault("lastname", "[не указано]");
        var age = args.GetPropertyValueOrDefault("age", 0).ToString();

        args.SetOrAddProperty(
            "guestModel",
            new GuestModel
            {
                Identification = $"{firstName} {lastName} ({age})"
            });

        return Done;
    }
}

/// <summary>
/// Когда данные прошли проверку и модель готова, нужно добавить модель в список гостей.
/// </summary>
public class AddGuestToList : SafeProcessor
{
    public override bool SafeCondition(PipelineContext args)
    {
        // Дополнительная проверка созданной модели.
        return base.SafeCondition(args) && args.ContainsProperty("guestmodel");
    }

    public override Task SafeExecute(PipelineContext args)
    {
        var guest = args.GetPropertyValueOrNull<GuestModel>("GuestModel");
        GuestList.Guests.Add(guest);

        return Done;
    }
}

Таким образом мы описали и реализовали бизнес-процесс шаг за шагом используя самостоятельные процессоры, каждый из которых отвечает за одну задачу. Поделитесь своими мыслями об этой статье в комментариях. И добро пожаловать на сайт https://wittylion.github.io/, где содержится больше примеров использования библиотеки, а так же другие интересные проекты.

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.