0

I am currently using MediatR to handle request/response cycles and wanted some ideas on how best to split an existing request handler which is now performing multiple operations.

The handler in question performs the following actions but may include others in the future:

  1. Validate form input
  2. Create new order
  3. Send a message to a queue (if message sending is successful, flag order accordingly)

As mentioned in previous SO posts (ref 1, ref 2) and GitHub issues, the preference is that a handler does not call other handlers to avoid tight coupling. Here below are the options that I am evaluating:

Pre/post-processors

I have a handler named PersistOrderHandler and I have attached a pre- and post-processor that perform validation and send the message to the queue. As of now, it is working fine based on the current set of actions, but my worry is that it will be hard to extend this to handle additional actions.

Notification

With the handler handling the creation of a new order, I would publish a message which is then picked up by an INotificationHandler to push the message into the queue. Since this method is sort of a "fire-and-forget" without tracking if such action was successful or not, it is not appropriate for the current scenario where I need to be sure that all the actions are completed successfully.

Behaviours

I've been looking into making use of Pipeline Behaviors, so I registered the following two behaviours (they will cater for actions 1 and 3 above; the main handler will be for action 2 above):

builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(SendMessageToQueueBehaviour<,>));

The chain of calls will be ValidationBehaviour -> PersistOrderHandler -> SendMessageToQueueBehaviour. Given the following code suggestion, to customise the pipeline to a specific handler, both the behaviours and the handler will implement an IOrderPersistencePipeline interface which contains a single "OrderId" property, meaning:

public interface IOrderPersistencePipeline
{
    int OrderId { get; set; }
}
public class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>, IOrderPersistencePipeline
{
}
public class RequestModel : IRequest<ResponseModel>, IOrderPersistencePipeline
{
    public int OrderId { get; set; }
    // ... other properties
}

public class PersistOrderHandler : IRequestHandler<RequestModel, ResponseModel>
{
    public async Task<bool> Handle(RequestModel request, CancellationToken cancellationToken)
    {
    }
}

public class ResponseModel
{
}

The limitation I'm finding with this approach is that due to its generic nature I'm unable to access the properties of the RequestModel during compile time within the behaviours unless I do some reflection (casting does not work). The "OrderId" in IOrderPersistencePipeline is a way I found that will allow me to have the "context" of the order being executed. Another way is to add all RequestModel properties in the IOrderPersistencePipeline but would like to avoid this.

Given all this, is the pipeline approach future-proof to be extended or the other approaches fit the scenario described? In the pipelines, how is it best to access the model properties given that TRequest is generic?

chris05
  • 735
  • 4
  • 13
  • 27

0 Answers0