Using the decorator pattern with Autofac
On a recent project I had to implement the decorator pattern to add some functionality to the existing code flow.
Not a big problem of course. However, on this project we were using Autofac for our dependency injection framework so I had to check how to implement this pattern using the framework built-in capabilities. One of the reasons I always resort to Autofac is the awesome and comprehensive documentation. It’s very complete and most of the time easy to understand. The advanced topics also have a chapter dedicated to the Adapter- and Decorator pattern which was very useful for implementing the decorator pattern in this project.
I wanted to use the decorator pattern to add some logic to determine if a command should be handled and for persisting database transactions of my commands and queries. You can also use it for things like security, additional logging, enriching the original command, etc.
As the documentation already states, you’ll have to register your original command handler as a Named service. The Autofac extensions for registering a decorator will use this named instance to add the decorators on to. One thing to remember when you need to add several decorators to your command, you’ll have to register each decorator as a named service also, except for the last one!
The command handlers we were using were accepting a generic argument to instantiate a class. Therefore, we also had to use the open generic version for registering the implementations and decorators.
The implementation of the actual command handler looks very much like the follwing code block.
public class ProcessedItemHandler<TCommand> : IProcessedMessageHandler<TCommand>
where TCommand : ProcessedMessageCommand
{
public ProcessedItemHandler(
IBackendSystemFormatter<TModel> formatter,
IQueueItemWriter<TModel> writer,
IRepository<ProcessQueue> processQueueRepository)
{
}
public void Handle(TCommand command)
{
/* Implementation logic */
}
}
It implements the IProcessedMessageHandler<TCommand>
interface and contains the logic to execute the command.
The decorator has to implement the same interface and one of the injected dependencies is the same interface. This tells Autofac to inject an IProcessedMessageHandler<TCommand>
which is ’linked’ in the registration of our application.
public class ProcessedMessageTransactionDecorator<TCommand> : IProcessedMessageHandler<TCommand>
where TCommand : ProcessedMessageCommand
{
private readonly IProcessedMessageHandler<TCommand> decorated;
private readonly ITransactionHandler transactionHandler;
public ProcessedMessageTransactionDecorator(
IProcessedMessageHandler<TCommand> decorated,
ITransactionHandler transactionHandler)
{
this.decorated = decorated;
this.transactionHandler = transactionHandler;
}
public void Handle(TCommand command)
{
/* Decorator logic */
decorated.Handle(command);
/* Decorator logic */
}
}
As you can see, you will be able to do all kinds of stuff in the Handle
-method before or after invoking the decorated object.
The registration in our application looks very much like the following code block.
var storeProcessedMessageCommandHandlers = GetAllStoreProcessedMessageCommandHandlerImplementationsFromAssemblies();
foreach (var commandHandler in storeProcessedMessageCommandHandlers)
{
builder.RegisterGeneric(commandHandler).Named("storeProcessedMessageHandler", typeof(IProcessedMessageHandler<>));
}
builder.RegisterGenericDecorator(typeof(ProcessedMessageTransactionDecorator<>), typeof(IProcessedMessageHandler<>),
fromKey: "storeProcessedMessageHandler");
First we need to collect all implementations of the IProcessedMessageHandler<TCommand>
and register them within the Autofac container. As you can see, all these implementations are registered as a named service with an index called storeProcessedMessageHandler
. If you only have 1 implementation of the command handler, you can just register this one implementation of course.
After having registered all of the command handlers, the decorator(s) can be registered. The helper method RegisterGenericDecorator
helps with this. This method also works with open generics and registration looks very similar to registering a ’normal’ class and interface. The main difference is the addition of the fromKey
argument. This argument is used to determine to which named service the decorator should be added to.
If you want to hook up multiple decorators you can also add the toKey
argument to your RegisterGenericDecorator
method. By adding the toKey
argument, the decorator is also added as a named service to Autofac and you will be able to hook up another decorator to the earlier decorator by using the name defined in the toKey
in the fromKey
of the new decorator. This might be a bit abstract, so let me just write up a small example.
builder.RegisterGeneric(typeof(IncomingHandler<>)).Named("commandHandler", typeof(ICommandHandler<>));
builder.RegisterGenericDecorator(typeof(TransactionRequestHandlerDecorator<>), typeof(ICommandHandler<>), fromKey: "commandHandler", toKey: "transactionHandler");
builder.RegisterGenericDecorator(typeof(ShouldHandleCommandHandlerDecorator<>), typeof(ICommandHandler<>), fromKey: "transactionHandler");
Makes more sense right?
Just remember, not to add a toKey
argument to the last decorator of your flow. Otherwise you will not be able to inject the interface, because everything is added to the IIndex<T>
collection and there isn’t a defined entrypoint. Ask me how I know.
Hope this helps you in future projects. Knowing about this functionality surely has helped me to keep the code clean.