DEV Community

Discussion on: Serverless Event-Driven Architecture on Azure: A Worked Example - Part 2

Collapse
 
colincmcc profile image
Colin McCullough • Edited

There isn't a current way to filter documents with the Change Feed trigger for Azure Functions. I'll try to find the GitHub issue, but this looks like the tracking request is here:
feedback.azure.com/forums/263030-a...

I've done this manually by implementing an interface (e.g. ICommandProcessor) with 2 methods: Handle & CanHandle. The command handler function, that's processing all commands, injects all handlers (IEnumerable ICommandProcessor ) and filters processing of each command based off the CanHandle method. I've implemented CanHandle both with reflection and direct lists of types. Reflection strategy grabs all parameters of methods named HandleAsync which implement ICommand to check against. Though, I don't like the idea of using reflection in Azure functions due to cold starts.

EDIT: Was on mobile previously, adding context
Here's an example (incomplete and probably has issues) of the command handler strategy based on Mario's post:

    public interface ICommandProcessor
    {
        Task HandleAsync(ICommand command, IAsyncCollector<Event> azureOutputBinding = null);
        bool CanHandle(ICommand command);
    }
Enter fullscreen mode Exit fullscreen mode
   public class ExampleCommandProcessor : ICommandProcessor
   {
      private readonly IEventStoreLogic _eventStore;

      private static readonly IEnumerable<Type> HandledCommands = typeof(ExampleCommandProcessor)
         .GetMethods(BindingFlags.Instance)
         .SelectMany(x => x.GetParameters()
            .Where(p => typeof(ICommand).IsAssignableFrom(p.ParameterType))
            .Select(t => t.ParameterType));

      public ExampleCommandProcessor(IEventStoreLogic eventStore)
      {
         _eventStore = eventStore;
      }

      public bool CanHandle(ICommand command)
      {
         return HandledCommands.Contains(command.GetType());
      }

      public Task HandleAsync(ICommand command, IAsyncCollector<Event> azureOutputBinding = null)
      {
         return HandleCommand((dynamic)command, azureOutputBinding);
      }

      private async Task HandleCommand(CreateItem command,
         IAsyncCollector<Event> azureOutputBinding = null)
      {
         if (azureOutputBinding != null) _eventStore.SetWritePersistence(azureOutputBinding);

         var itemStream = await _eventStore.LoadAggregateStreamAsync(command.AggregateId);

         if (itemStream .Any()) throw new SomeRandomException();

         var newItem = Item.AddOrSomething(command); // static in this example

         await UpdateStream(newItem);
      }
... continue multiple handlers with concrete commands
Enter fullscreen mode Exit fullscreen mode

If you need more guidance on it, I'd look at in-process messaging platform strategies, like Mediatr or MassTransit's Mediator. They perform a lot of black box magic behind the scenes, that have a similar concept.