So let's talk about Ninject and how to bind decorators, I will illustrate it decorating Command Handlers in a CQRS architecture
Using this pattern will allow you to follow the Single Responsibility Principle in your command handlers.
Why? Let's take a look
So a typical command handler will validate its command and to achieve this we have several options:
Inside the command
public class GrantAccessToNightClubCommandHandler : ICommandHandler<GrantAccessToNightClub>
{
public void Handle(GrantAccessToNightClub command)
{
if (command.Age < 21)
{
throw new InvalidOperationException("Age was not satisfied");
}
//do stuff
}
}
So I don't like this approach because the validation is lost inside the handlers take a look to Make roles explicit by Udi Dahan
So let's try another approach
Injecting validators into the command handler
public class GrantAccessToNightClubCommandHandler : ICommandHandler<GrantAccessToNightClub>
{
private readonly IValdiatorRunner<GrantAccessToNightClub> _valdiatorRunner;
public GrantAccessToNightClubCommandHandler(IValdiatorRunner<GrantAccessToNightClub> valdiatorRunner)
{
_valdiatorRunner = valdiatorRunner;
}
public void Handle(GrantAccessToNightClub command)
{
_valdiatorRunner.Run(command);
//valid at this point
//do stuff
}
}
I won't go into detail about the IValidatorRunner
implementation, but it loads dynamically validators for the specified command and runs them all
I used to like this technique since it provides a good separation of concerns, making the roles explicit but still... it looks like the command handler is doing too much, it's not following the SRP
Let's see the third option
Using Decorators
public class CommandHandlerValidator<TCommand> : ICommandHandler<TCommand> where TCommand : ICommand
{
private readonly IEnumerable<IValdiator<TCommand>> _valdiators;
private readonly ICommandHandler<TCommand> _commandHandler;
public CommandHandlerValidator(
IEnumerable<IValdiator<TCommand>> valdiators,
ICommandHandler<TCommand> commandHandler)
{
_valdiators = valdiators;
_commandHandler = commandHandler;
}
public void Handle(TCommand command)
{
_valdiators.ToList().ForEach(x => x.ApplyTo(command));
// wow everything is valid here, lets continue
_commandHandler.Handle(command);
}
}
public class GrantAccessToNightClubCommandHandler : ICommandHandler<GrantAccessToNightClub>
{
public void Handle(GrantAccessToNightClub command)
{
//do stuff
}
}
Wow, now it looks like we are following the SRP therefore everything starts to look simpler and easier to maintain and extend. Just by following the SRP. Wow it blows my mind
Now imagine that we want to apply authorization to our command handlers, again we can solve this by decorating them like:
public class CommandHandlerAuthenticator<TCommand> : ICommandHandler<TCommand> where TCommand : ICommand
{
private readonly ICommandHandler<TCommand> _commandHandler;
public CommandHandlerAuthenticator(ICommandHandler<TCommand> commandHandler)
{
_commandHandler = commandHandler;
}
public void Handle(TCommand command)
{
Authenticate(command);
//when authenticated simply...
_commandHandler.Handle(command);
}
private void Authenticate(TCommand command)
{
// some authentication
}
}
Now the trick is to inject the Decorators. I'm a big fan of Ninject so I will show you how to do it the Ninja style.
You need to define the injectors in the order that you want them, if you take a closer look to the previous code again, we have not specified what concrete command handler is going to be injected into the CommandHandlerValidator
or the CommandHandlerAuthenticator
. All we've defined is that a ICommandHandler
will be injected somehow.
All this configuration will be done via our container. It's awesome
//we bind everythig except our command handlers since we will treat them as decorators
kernel.Bind(config =>
{
config.From(fromTypes.Select(x => x.Assembly))
.SelectAllClasses()
.Where(x => !x.IsAssignableToGenericType(typeof(ICommandHandler<>)))
.BindAllInterfaces();
});
kernel.Bind(config =>
{
config.From(fromTypes.Select(x => x.Assembly)).SelectAllClasses()
.Where(x => x.IsAssignableToGenericType(typeof(ICommandHandler<>)))
.BindAllInterfaces().Configure(x => x.WhenInjectedInto(typeof(CommandHandlerValidator<>)));
});
kernel.Bind(typeof(ICommandHandler<>)).To(typeof(CommandHandlerValidator<>))
.WhenInjectedInto(typeof(CommandHandlerAuthenticator<>));
kernel.Bind(typeof(ICommandHandler<>)).To(typeof(CommandHandlerAuthenticator<>));
This code can be read as follows (from bottom to top):
- When I ask an
ICommandHandler
give meCommandHandlerAuthenticator
- Then when I'm injecting a
CommandHandlerAuthenticator
give me aCommandHandlerValidator
- And finally, when I'm injecting a
CommandHandlerValidator
give me myICommandHandler
It's tricky I know... but it works great
This code could be improved maybe creating an Extension Method to bind decorators.
So when you test it like this:
var guard = kernel.Get<ICommandHandler<GrantAccessToNightClub>>();
guard.Handle(new GrantAccessToNightClub("jp", 21));
This is the output:
Test Name: TestMethod1
Test Outcome: Passed
Result StandardOutput:
Authenticating...
Valdiating: 21: Granted
Valdiation done, lets continue
Finally doing some work here...
Now the fun part, TEST IT
I don't know how to call this kind of test, I don't believe it's a unit test, integration test perhaps uhmm well I just call them Wiring tests
[TestMethod]
public void it_should_resolve_the_CommandHandlerDecorators()
{
var kernel = new StandardKernel();
var fromTypes = new[]
{
typeof (DecoratorTests)
};
//we bind everythig except our command handlers since we will treat them as decorators
kernel.Bind(config =>
{
config.From(fromTypes.Select(x => x.Assembly))
.SelectAllClasses()
.Where(x => !x.IsAssignableToGenericType(typeof(ICommandHandler<>)))
.BindAllInterfaces();
});
kernel.Bind(config =>
{
config.From(fromTypes.Select(x => x.Assembly)).SelectAllClasses()
.Where(x => x.IsAssignableToGenericType(typeof(ICommandHandler<>)))
.BindAllInterfaces().Configure(x => x.WhenInjectedInto(typeof(CommandHandlerValidator<>)));
});
kernel.Bind(typeof(ICommandHandler<>)).To(typeof(CommandHandlerValidator<>))
.WhenInjectedInto(typeof(CommandHandlerAuthenticator<>));
kernel.Bind(typeof(ICommandHandler<>)).To(typeof(CommandHandlerAuthenticator<>));
var field = typeof(KernelBase).GetField("bindings",
BindingFlags.Instance | BindingFlags.NonPublic);
field.Should().NotBeNull();
var bindingsMap = field.GetValue(kernel) as Multimap<Type, IBinding>;
bindingsMap.Should().NotBeNull();
bindingsMap.Should().NotBeEmpty();
var commandHandlerBindings = bindingsMap.SelectMany(x => x.Value)
.Where(x => x.Service.IsAssignableToGenericType(typeof(ICommandHandler<>))).ToList();
commandHandlerBindings.Should().NotBeNullOrEmpty();
commandHandlerBindings.Where(x => !x.Service.IsGenericTypeDefinition).ToList().ForEach(x =>
{
var handlers = kernel.GetAll(x.Service);
foreach (var instance in handlers)
{
instance.GetType().IsAssignableToGenericType(typeof(CommandHandlerAuthenticator<>)).Should().BeTrue();
// testing that the CommandHandlerValdiator was injected into CommandHandlerAuthenticator
VerifyInnerCommandHandler(instance, x.Service, typeof (CommandHandlerValidator<>),
typeof (CommandHandlerAuthenticator<>));
var innerCommandHandler = instance.GetType().GetField("_commandHandler",
BindingFlags.Instance | BindingFlags.NonPublic).GetValue(instance);
// testing that the ICommandHandler was injected into CommandHandlerValdiator
VerifyInnerCommandHandler(innerCommandHandler, x.Service, typeof (ICommandHandler<>),
typeof (CommandHandlerAuthenticator<>), typeof (CommandHandlerValidator<>));
}
});
}
private void VerifyInnerCommandHandler(object instance, Type service, Type expectedType, params Type[] notExpectedTypes)
{
var innerHandlerField = instance.GetType().GetField("_commandHandler",
BindingFlags.Instance | BindingFlags.NonPublic);
innerHandlerField.Should().NotBeNull();
var innerHandler = innerHandlerField.GetValue(instance);
innerHandler.GetType().IsAssignableToGenericType(expectedType).Should().BeTrue();
foreach (var notExpectedType in notExpectedTypes)
{
innerHandler.GetType().IsAssignableToGenericType(notExpectedType).Should().BeFalse();
}
Console.WriteLine("{0} should be: {1}", innerHandler.GetType(), service);
service.IsInstanceOfType(innerHandler).Should().BeTrue();
service.IsAssignableFrom(innerHandler.GetType()).Should().BeTrue();
innerHandler.GetType().IsAssignableToGenericType(expectedType).Should().BeTrue();
}
Ohh almost forgot, I'm using the following Type
extension: IsAssignableToGenericType
There you go
Hope you have enjoyed it
No comments:
Post a Comment
If you found this post useful please leave your comments, I will be happy to hear from you.