-
Notifications
You must be signed in to change notification settings - Fork 4
Home
PipelineRD is a library that implements the chain of responsability pattern and brings to you many features to make your code more resilient, readable and easier to maintain. Supports netstandard2.0
and netstandard2.1
.
This library is an improved version of the PipelineR, refactored and better to use it.
Install the package via NuGet first:
Install-Package PipelineRD
PipelineRD has some dependencies such as:
- Polly: used to implement the retry policy to the steps.
- DiagramBuilder: custom library made by us to help the building of the visual documentation using Mermaid-JS, a markdown diagramming and chart tool.
- Fluent Validation: to add a validator to the pipeline and use it to implement the fail fast principle.
- Serilog: to log a possible pipeline error.
You will need to configure the pipeline using the extension method IServiceCollection.UsePipelineRD
. It's action methods allow you to register the type of cache that you will use, the dependency injection of all pipeline services and the documentation that will be generated after each run.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.UsePipelineRD(x =>
{
// ...
});
}
You can use two types of cache, in memory and redis. Both cache settings contains a TTLInMinutes
property that will hold the value to how many minutes the cache will get hold in place and a KeyPreffix
that will be used to preffix the cache data key. The redis settings contains a connection string property that will be used to connect.
For now, the usage of cache is obrigatory, because the pipeline uses it to go back to a previous state based on a hash that is generated by the json serialized request concatenated with the name of the pipeline.
services.UsePipelineRD(x =>
{
x.UseCacheInMemory(new MemoryCacheSettings());
// Or
x.UseCacheInRedis(new RedisCacheSettings());
});
If you prefer, the library contains a built in automatic service injection to the .NET dependency container. You can choose individually which service type to inject. Of course, you can choose to inject all of them at the same time.
All services will be injected with the lifetime scoped, but the validators are singleton.
services.UsePipelineRD(x =>
{
x.AddPipelineServices(x =>
{
x.InjectContexts();
x.InjectSteps();
x.InjectPipelines();
x.InjectPipelineInitializers();
x.InjectPipelineBuilders();
// Or all
x.InjectAll();
});
});
The library brings a built in visual diagram html documentation of the pipelines implemented in your application. To use this option, you will have to implement the interface IPipelineBuilder
and write the pipeline code in a method inside it. It is necessary because we use reflection to find all pipeline builders and execute it's methods in runtime to generate the source html files. You can check an example of this in the sample project.
You can choose to save in a folder path, or integrate with the static files server from .NET Core like the example.
The html files are a simple index.html with some javascript and css files.
services.UsePipelineRD(x =>
{
x.UseDocumentation(x =>
{
// localhost:{PORT}/docs (If you use the static files server)
var path = Path.Combine(Environment.ContentRootPath, "wwwroot", "docs");
x.UsePath(path);
// Or a normal directory.
x.UsePath(@"C:\dev\docs");
});
});
Example of the sample project pipeline diagram
![](https://user-images.githubusercontent.com/29133996/134798108-34443259-1b1e-406c-b6c9-1e87ff4ae480.png)
You can either use by dependency injection IPipeline
or directly instanciation Pipeline
. The way you build the pipeline is using fluent methods.
Method that will add a new step in to the pipeline. It will handle a step that implements the interface IStep
, either being the default IRequestStep
or IAsyncRequestStep
.
Method that proceeds the method AddNext
. It will receive a lambda that handles the context object of the pipeline and you can create conditions to execute the step defined previously.
Method that will add a rollback step in to the pipeline. It will handle a step that implements the interface IStep
, either being the default IRollbackRequestStep
or IAsyncRollbackRequestStep
.
Method that will start the execution of the pipeline. It receives a object that can be of any type and will be used by the pipeline step as the main model.
Simple one step pipeline initialization
var result = await Pipeline
.AddNext<ICustomStep>()
.Execute(model);
Multiple steps pipeline initialization
var result = await Pipeline
.AddNext<ICustomOneStep>()
.AddNext<ICustomTwoStep>()
.Execute(model);
Multiple conditional steps pipeline initialization
var result = await Pipeline
.AddNext<ICustomOneStep>()
.AddNext<ICustomTwoStep>()
.When(b => b.Id == "testOne")
.AddNext<ICustomTwoStep>()
.When(b => b.Id == "testTwo")
.Execute(model);
It is a shared model between the pipeline steps. You can use to share variables. It needs to implement the abstract class BaseContext
.
You can define a step by extending the classes RequestStep<TContext>
or AsyncRequestStep<TContext>
and implementing either IRequestStep
or IAsyncRequestStep
depending of the step type.
Method that returns the sent model configured in the Execute
method when initializing the pipeline.
Method that handles the advance of the pipeline. It will be used when you want to proceed to the next step.
Method that handles the abortion of the pipeline. It will be used when you want to abort the execution. It is used together with the return keyword and receives the object RequestError
.
Method that handles the ending of the pipeline. It will be used when you want to end the execution and return a result. It is used together with the return keyword.
Simple sync request step
public class SimpleCustomStep : RequestStep<CustomContext>, ISimpleCustomStep
{
public override RequestStepResult HandleRequest()
{
var model = this.Request<ModelType>();
Console.WriteLine("SimpleCustomStep");
// ...
return this.Next();
}
}
public interface ISimpleCustomStep : IRequestStep<CustomContext> { }
Simple async request step
public class SimpleCustomStep : AsyncRequestStep<CustomContext>, ISimpleCustomStep
{
public override async Task<RequestStepResult> HandleRequest()
{
var model = this.Request<ModelType>();
Console.WriteLine("SimpleCustomStep");
// ...
return this.Next();
}
}
public interface ISimpleCustomStep : IAsyncRequestStep<CustomContext> { }
Request step with abort
public class SimpleCustomStep : AsyncRequestStep<CustomContext>, ISimpleCustomStep
{
public override async Task<RequestStepResult> HandleRequest()
{
var model = this.Request<ModelType>();
Console.WriteLine("SimpleCustomStep");
// ...
if(model.type == "inactive")
{
var error = new RequestError("Inactive");
return this.Abort(error, HttpStatusCode.BadRequest);
}
return this.Next();
}
}
public interface ISimpleCustomStep : IAsyncRequestStep<CustomContext> { }