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/, где содержится больше примеров использования библиотеки, а так же другие интересные проекты.