I am using CQRS design pattern. I have more than 15 command handlers corresponding to each event type. I want to avoid below switch case to call corresponding command handler based on event type.
Here is my Azure Function:
[FunctionName("ReceiveEvent")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
ILogger log)
{
//log.LogInformation("ReceiveEvent HTTP trigger function started processing request.");
//log.LogInformation($"Pushing Events to Azure Blob on storage account :-{CloudConfigurationManager.GetSetting("AzureWebJobsStorage")}");
IActionResult actionResult = null;
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var command = await _commandMapper.Map(requestBody);
if (_commandValidator.Validate(req, command, ref actionResult))
{
switch (command.EventType)
{
case EventType.CARD_BLOCK:
_cardBlockCommandHandler.Handle(command as CardBlockCommand);
break;
case EventType.CARD_CANCEL:
_cardCancelledCommandHandler.Handle(command as CardCancelledCommand);
break;
case EventType.CARD_UNBLOCK:
_cardUnBlockHandler.Handle(command as CardUnBlockCommand);
break;
}
//TODO
return actionResult;
}
Is there any better way to avoid this switch case?
Command handler :
public class CardBlockCommandHandler : ICommandHandler<CardBlockCommand>
{
private readonly IAzureBlobStorage _azureBlobStorage;
public CardBlockCommandHandler(IAzureBlobStorage azureBlobStorage)
{
_azureBlobStorage = azureBlobStorage;
}
public void Handle(CardBlockCommand command)
{
try
{
//TODO: Store into blob
//_azureBlobStorage.UploadMessageContentAsync(storageConnectionString: string.Empty,
// storageContainerName: string.Empty, blobName: string.Empty, content: string.Empty);
throw new NotImplementedException();
}
catch
{
}
}
}
ICommandHandler:
public interface ICommandHandler<TCommand> where TCommand : ICommand
{
/// <summary>
/// Execute command
/// </summary>
/// <param name="command"></param>
void Handle(TCommand command);
}
Command:
public abstract class Command : ICommand
{
public EventType EventType { get; }
}
public interface ICommand
{
EventType EventType { get; }
}
This is elaborating on what was mentioned in the comments about using a service. Have a ICommandHandlerService which is injected into your function constructor as a dependency (you are using a DI container aren't you ?). This interface would have the following method:
void HandleCommand(ICommand command);
The implementation of ICommandHandlerService would have a Dictionary mapping between event type and CommandHandlers such as
public class CommandHandlerService : ICommandHandlerService
{
Dictionary<EventType, ICommandHandler> handlerDictionary = new Dictionary<EventType, ICommandHandler>();
//assuming that all your different handlers implement ICommandHandler ?
public void CommandHandlerService()
{
handlerDictionary.Add(EventType.CARD_UNBLOCK, new CardUnBlockCommandHandler());
handlerDictionary.Add(EventType.CARD_BLOCK, new CardBlockCommandHandler());
//setup rest of your associations
}
}
void HandleCommand(ICommand command)
{
if(!handlerDictionary.ContainsKey(command.EventType))
{
//throw suitable exception ?
}
var commandHandler = handlerDictionary[command.EventType];
commandHandler.Handle(command);
}
Your Azure function constructor would be passed in a ICommandHandlerService instance by your DI framework and you would call it so:
if (_commandValidator.Validate(req, command, ref actionResult))
{
commandHandlerService.Handle(command);
}