Fork me on GitHub

Message Handlers Edit on GitHub


It is perfectly valid usage to use more than one handler method for any message type. While the FubuMVC team doesn't necessarily recommend this in most cases, it may be useful for reusing some generic logic or creating extensibility mechanisms in your application.

Sending messages back and forth is all fine and good, but one way or another, something has to actually handle and process those messages. Inside of FubuMVC, message handling is done with handler actions. Handler actions are nothing more than a public method on a public type that has a single input parameter for the message type that it processes:


public class SimpleHandler
{
    public void Handle(PingMessage message)
    {
        Console.WriteLine("I got a ping!");
    }
}

Note that there is absolutely no mandatory FubuMVC-specific interfaces or attributes or base types. One of FubuMVC's primary design objectives from the beginning was to eliminate direct coupling from your application code to FubuMVC itself. FubuMVC has also strived to reduce the amount of mandatory cruft in your application code like attributes, fluent interfaces, marker interfaces, and mandatory base classes that are so prevalent in many other frameworks in the .Net space.

Jasper will support method injection to allow you to take in additional service dependencies in addition to the message body itself in the handler methods to potentially simplify your application code.

See also Cascading Messages for other valid handler method signatures.

Asynchronous Handlers

FubuMVC 3.0's async support is charitably described as "imperfect." FubuMVC 4.0 will be rolled out soon with a truly "async all the way down" runtime model as a stopgap solution before "Jasper."

If you're trying to be more efficient at runtime by taking advantage of asynchronous processing, you can make the signature of your handler action methods return a Task or Task<T> if you're exposing a cascading message.

A sample, async handler method is shown below:


public interface IPongWriter
{
    Task WritePong(PongMessage message);
}

public class AsyncHandler
{
    private readonly IPongWriter _writer;

    public AsyncHandler(IPongWriter writer)
    {
        _writer = writer;
    }

    public Task Handle(PongMessage message)
    {
        return _writer.WritePong(message);
    }
}

Handler Dependencies

At runtime, the handler objects are created by the underlying StructureMap container for your application, meaning that you can inject service dependencies into your handler objects:


public interface IMyService
{

}

public class ServiceUsingHandler
{
    private readonly IMyService _service;

    // Using constructor injection to get dependencies
    public ServiceUsingHandler(IMyService service)
    {
        _service = service;
    }

    public void Consume(PingMessage message)
    {
        // do stuff using IMyService with the PingMessage
        // input
    }
}

The handler objects are built by a nested container scoped to the current message.

See Integration with StructureMap for IoC for more information.

How FubuMVC Finds Handlers

FubuMVC uses StructureMap 4.0's type scanning support to find handler classes and candidate methods from known assemblies based on naming conventions.

By default, FubuMVC is looking for public classes in the main application assembly with names matching these rules:

  • Type name ends with "Handler"
  • Type name ends with "Consumer"
  • Type closes the open generic type IStatefulSaga<T> for classes implementing sagas

From the types, FubuMVC looks for any public instance method that accepts a single parameter that is assumed to be the message type.

Customize Handler Discovery

Do note that handler finding conventions are additive, meaning that adding additional handler sources doesn not automatically disable the built in handler source. This is different than the action discovery in the HTTP side of FubuMVC.

The easiest way to use the FubuMVC service bus functionality is to just code against the default conventions. However, if you wish to deviate from those naming conventions you can either supplement the handler discovery or replace it completely with your own conventions.

At a minimum, you can disable the built in discovery, add a new handler source through the FindBy() methods, or register specific handler classes with the code below:


public class CustomHandlerApp : FubuTransportRegistry<AppSettings>
{
    public CustomHandlerApp()
    {
        // Turn off the default handler conventions
        // altogether
        Handlers.DisableDefaultHandlerSource();

        // Custom handler finding through common options
        Handlers.FindBy(source =>
        {
            source.UseThisAssembly();
            source.IncludeClassesSuffixedWithHandler();
        });

        // Include candidate methods from a specific class or
        // classes
        Handlers.Include(typeof(SimpleHandler), typeof(AsyncHandler));

        // Include a specific handler class with a generic argument
        Handlers.Include<SimpleHandler>();

    }
}

If you want to build your own custom handler source, the easiest thing to do is to subclass the HandlerSource class from FubuMVC and configure it in its constructor function like so:


public class MyHandlerSource : HandlerSource
{
    public MyHandlerSource()
    {
        // Search through other assemblies
        UseAssembly(typeof(PingMessage).GetTypeInfo().Assembly);

        // And the application assembly
        UseThisAssembly();


        // Use a custom filter to determine handler types
        IncludeTypes(type => type.Closes(typeof(IHandler<>)));

        // Or look for types with a known suffix
        IncludeClassesSuffixedWith("Handler");

        // Or use some built in filters
        IncludeClassesSuffixedWithConsumer();
    }
}

If you want to go off the beaten path and do some kind of special handler discovery, you can directly implement the IHandlerSource interface in your own code.

Let's say that you're converting an application to FubuMVC that previously used a service bus that exposed message handling through an interface like IHandler shown below:


public interface IHandler<T>
{
    void Handle(T message);
}

An implementation of a custom action source that can discover IHandler<T> classes and methods may look like the following:


public class MyCustomHandlerSource : IHandlerSource
{
    public async Task<HandlerCall[]> FindCalls(Assembly applicationAssembly)
    {
        var types = await TypeRepository
            .FindTypes(applicationAssembly, TypeClassification.Concretes | TypeClassification.Closed);

        var candidates = types.Where(x => x.Closes(typeof(IHandler<>)));

        return candidates
            .SelectMany(findCallsPerType)
            .ToArray();
    }

    private static IEnumerable<HandlerCall> findCallsPerType(Type type)
    {
        return type
            .GetMethods()
            .Where(m => m.Name == nameof(IHandler<object>.Handle))
            .Select(m => new HandlerCall(type, m));
    }
}

Finally, you can direct FubuMVC to use your custom handler sources with code like this inside of your FubuTransportRegistry class representing your application:


public class AppWithCustomHandlerSources : FubuTransportRegistry<AppSettings>
{
    public AppWithCustomHandlerSources()
    {
        // Add a source by type
        Handlers.FindBy<MyHandlerSource>();

        // Add a source object directly
        Handlers.FindBy(new MyCustomHandlerSource());
    }
}

Subclass or Interface Handlers

FubuMVC will allow you to use handler methods that work against interfaces or abstract types to apply or reuse generic functionality across messages. Let's say that some subset of your messages implement some kind of IMessage interface like this one and an implentation of it below:


public interface IMessage { }

public class MessageOne : IMessage { }

You can handle the MessageOne specifically with a handler action like this:


public class SpecificMessageHandler
{
    public void Consume(MessageOne message)
    {

    }
}

You can also create a handler for IMessage like this one:


public class GenericMessageHandler
{
    private readonly Envelope _envelope;

    public GenericMessageHandler(Envelope envelope)
    {
        _envelope = envelope;
    }

    public void Consume(IMessage message)
    {
        Console.WriteLine($"Got a message from {_envelope.Source}");
    }
}

When FubuMVC handles the MessageOne message, it first calls all the specific handlers for that message type, then will call any handlers that handle a more generic message type (interface or abstract class most likely) where the specific type can be cast to the generic type. You can clearly see this behavior by examining the handler chain diagnostics.