EasyEvs - Series 3

Thu, Sep 9, 2021 2-minute read

EasyEvs in a few minutes

Actions and Pipelines

So now you have your events published and handled, you are happy with the current logic, but a new requirement arrives:

You have to measure the time of the execution of your handler to provide metrics.

So you want to this without modifying your existing code, because

  • Why risking to change and introduce a problem?
  • Is not the handler reponsibility to keep track of the time.
  • You don’t want to change all of your unit tests.

Good! Then Pipelines come to the rescue.

Simply implement the interface IPipelineHandlesEventAction<T> and register it in the ServiceCollection, something like this:

public class MetricsPipeline : IPipelineHandlesEventAction<OrderCreated>
{
  public async Task<OperationResult> Execute(
      OrderCreated @event,
      IConsumerContext context, 
      Func<Task<OperationResult>> next, 
      CancellationToken cancellationToken)  
  {
    var time = new Stopwatch();
    time.Start();
    var result = await next();
    time.Stop();
    Console.WriteLine($"OrderCreated processed in {time.ElapsedMilliseconds} ms");
    return result;
 }
}

That’s it! No code modified, all you did is add a Pipeline that will execute code before and after your handler (or another pipeline.)

Func<Task<OperationResult>> next

Is a function that will be called (if you want to) and will execute the next part of the chain of execution. If there are no more Pipelines Registered, it will be the Handler, otherwise the next pipeline.

The Pipelines MUST be registered in the ServiceCollection explicitely, and that will determine the order of execution, so


services.AddScoped<IPipelineHandlesEventAction<OrderCreated>, ExceptionHandlerPipeline>();
services.AddScoped<IPipelineHandlesEventAction<OrderCreated>, MetricsPipeline>();

Will make the Exception Handler to wrap the call of the Metrics and the Handler, this is ideal if you want to ensure no exceptions are being thrown, and you login everything.

The library also provides the following interfaces

  • public interface IPostHandlesEventAction<T>

  • public interface IPretHandlesEventAction<T>

when implemented they will allow to execute code before and after the event is handled respectively.

Example

public class PostAction1 : IPostHandlesEventAction<OrderCreated>
{
    Task<OperationResult> Execute(OrderCreated @event, IConsumerContext context, OperationResult result, CancellationToken cancellationToken)
    {        
        // If the Id was invalid we change the result
        if(result != Ok && @event.orderId == Guid.Empty)
        {
            retuen Task.FromResult(OperationResult.Ok);
        }

        return result;
    }