Jul
25

Refactoring to improve maintainability & blendability using IoC part 2: Reducing IoC container coupling

By kellabyte  //  Architecture  //  15 Comments

Recap of part 1

In part 1 of this series we started reducing the Service Locator calls by removing them from the view models and moving it to the ViewModelFactory since this class is responsible for creating the view models. This allowed us to cut the ties in the view models where they were requesting dependencies from a global container.

Some of the benefits we gained from the first iteration of refactoring:

  1. Reduced coupling between view models and the IoC container.
  2. View model dependencies are push not pull. This removes coupling between the view model and the source of the dependencies.
  3. View models are more intention revealing. What view models depend on and require is explicit and not hidden.
  4. View models are easier to test. An IoC container isn’t required to initialize a unit test.

After all of the changes in the first iteration we still retained Blendability and did not have to compromise the design time experience.

This still left us with a global container which the ViewModelFactory calls to resolve dependencies for view models. Once our view models were up and running again it was time to remove the global container and dramatically reduce the coupling.

Why a type asking the IoC container to resolve its dependencies is bad

Having view models use a Service Locator or request the IoC container to compose the dependencies on its behalf couples the view model to the source of its dependencies. The view model does not need the IoC container itself to do any work. It only needs its dependencies. The view model requesting the dependencies has created a requirement on the view model for the IoC container to exist although it is not needed when you look at what the view model is responsible for.

This is brittle and instead should be handled declaratively. With service locator the view model is dependent on the IoC container which needs to be exposed as global state (so that it is accessible) whereas with dependency injection any IoC container can handle the creation.

This includes your tests. The global container is required for your objects to do their work. With dependency injection, testing your view models becomes simpler.

My original idea to minimize the coupling to a specific IoC container was to introduce IServiceLocator so that I could create implementations for various containers like Funq and Unity. What I found out was that it was easy to abstract the resolve methods of these IoC containers but abstracting the register methods was proving to be far more difficult. I was experiencing a lot of friction and it wasn’t clear how I could achieve this.

I learned a couple more things from this experience. It’s really difficult to abstract 2 things that do similar things but are implemented differently. The abstraction can become complex and the benefits start to fade.

What we did about the IoC container

As mentioned above rather than abstract the IoC containers with a layer of API that looks just like another IoC container we took the approach of encapsulation. Encapsulating the IoC container instead of abstracting it allowed us to still retain the ability to create new implementations based on different IoC containers but remove the coupling throughout the code base.

We introduced 2 key things to contribute to removing dependency on the IoC container. A Composition Root and ViewModelResolver.

Composition root

The concept of a composition root comes from Mark Seemann. He writes about it in his excellent book Dependency Injection in .NET. I highly recommend reading it. You won’t regret it!

Mark writes:

DEFINITION A Composition Root is a (preferably) unique location in an application where modules are composed together.

A DI Container should only be referenced from the Composition Root. All other modules should have no reference to the container.

We had to bend the rules a little bit to allow for ViewModelResolver but I think you will find it an acceptable solution because we closed off any abilities of container abuse.

If you recall earlier we had an ApplicationHost.Current singleton which exposed an ApplicationHost.Current.Container property so that we could resolve abuse dependencies anywhere we wanted. We replaced this with the Composition Root which we named Bootstrapper.

Before

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
public class ApplicationHost
{
private static volatile ApplicationHost instance;
private static object syncRoot = new Object();
 
private ApplicationHost()
{
Execute.InitializeWithDispatcher();
 
// Initialize the Funq container.
this.Container = new Container();
}
 
public static ApplicationHost Current
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new ApplicationHost();
}
}
return instance;
}
}
 
public Container Container { get; private set; }
}

As you can see in the singleton I exposed the container making it available for abuse to people writing code against it and coupling to it all over the place. Things can get out of control fast and become difficult to maintain and not flexible to change going forward.

After

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
public class Bootstrapper
{
private Container container = null;
 
public Bootstrapper()
{
Initialize();
}
 
private void Initialize()
{
container = new Container();
 
container.Register<IEventAggregator>(new EventAggregator());
container.Register<IDatabase>(new Database());
 
container.Register<MainViewModel>(c => new MainViewModel(
c.Resolve<IEventAggregator>(),
c.Resolve<IDatabase>()));
 
container.Register<BrowserViewModel>(c => new BrowserViewModel());
 
container.Register<SettingsViewModel>(c => new SettingsViewModel(
c.Resolve<IDatabase>()));
}
}

In the Bootstrapper which is our Composition Root, we create the object graph dependencies in the Initialize() method. If you recall in part 1 we modified view models to support constructor injection. Those constructor parameters map to this composition we declare in the Bootstrapper. The types we define will get injected in the constructors of the view models when the IoC container resolves them.

It is important all your dependencies are declared in the Composition Root or else you will get a failure when you try to resolve one of the view models.

Also very key is we have not exposed the IoC container at all. We are completely closing off the ability for arbitrary code being able to call the container directly.

In the App.cs we instantiate the Bootstrapper to kick off the container registrations.

One thing still remains that is referencing the IoC container. In part 1 we moved the container resolve calls from the view models to the ViewModelFactory in our iterative refactoring. Now that view models are not aware of the IoC container and we have a Composition Root in place we need to find a solution to remove the coupling between ViewModelFactory and the IoC container.

We did this by introducing IViewModelResolver.

IViewModelResolver

My original idea of abstracting the IoC container into an IServiceLocator interface introduced an array of problems such as:

  1. Still coupling view models to a generic Service Locator interface. View models still require a Service Locator to do work.
  2. Sacrificing the power of each IoC implementation because the abstract layer on top would be a compromised feature set.
  3. Fighting a lot of friction trying to make API’s that aren’t common work in a common way.

IViewModelResolver allows us to do the opposite. We can encapsulate the IoC container implementation and leverage that IoC containers full feature set because we aren’t exposing it. We can also implement different IoC container types easily.

In the Composition Root we declared our view models and their dependency composition. The view model resolvers will be used by the ViewModelFactory to resolve the view models. The view models just as before will be injected by their constructors.

As a side benefit we are achieving a better level of Single Responsibility Principle because the ViewModelFactory is responsible for providing run time or design time view models and the view model resolvers are responsible for resolving dependencies against a specific IoC container.

The resolver is also very easy to mock making it easier to test the ViewModelFactory than it was when we had the globally exposed IoC container with Service Locator requests.

In our case since we are using Funq, we created a FunqViewModelResolver implementation of IViewModelResolver.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
public interface IViewModelResolver
{
IViewModel Resolve();
}
 
public class FunqViewModelResolver : IViewModelResolver
{
private readonly Container container;
private Dictionary<type , MethodInfo> methods = null;
private MethodInfo method = null;
 
public FunqViewModelResolver(Container container)
{
this.container = container;
 
method = container.GetType().GetMethod(
"Resolve", new Type[0]);
 
methods = new Dictionary<Type,MethodInfo>();
}
 
public IViewModel Resolve(Type viewModelType)
{
MethodInfo genericMethod = null;
if (!methods.TryGetValue(viewModelType, out genericMethod))
{
genericMethod = method.MakeGenericMethod(viewModelType);
methods.Add(viewModelType, genericMethod);
}
 
var result = genericMethod.Invoke(container, null);
return (IViewModel) result;
}
}

Now that the Funq version of the resolver is implemented we need to refactor ViewModelFactory again to use the resolvers instead of the Service Locator call that we broke removed when we deleted the globally exposed IoC container.

Before

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
public abstract class ViewModelFactory<TViewModel, TDesignViewModel>
where TViewModel : IViewModel
where TDesignViewModel : TViewModel, new()
{
public ViewModelFactory()
{
}
 
public TViewModel ViewModel
{
get
{
TViewModel viewModel;
bool designMode = DetectDesignMode();
 
if (designMode)
{
// At design time instantiate design time version.
// Populates mock data, dependencies don't have to be real.
viewModel = new TDesignViewModel();
}
else
{
// Request IoC container to resolve view model & its deps.
viewModel =
ApplicationHost.Current.Container.Resolve<TViewModel>();
}
 
viewModel.OnLocated();
return viewModel;
}
}
}

After

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
public abstract class ViewModelFactory
{
protected static IViewModelResolver resolver = null;
public static void InitializeResolver(IViewModelResolver resolver)
{
// This is where we set the resolver for the ViewModelFactory.
// This should get set in the Bootstrapper (Composition Root)
// to the resolver implementation using the
// IoC container that has been chosen for the application.
ViewModelFactory.resolver = resolver;
}
}
 
public abstract class ViewModelFactory<TViewModel, TDesignViewModel>
: ViewModelFactory
where TViewModel : IViewModel
where TDesignViewModel : TViewModel, new()
{
public ViewModelFactory()
{
}
 
public TViewModel ViewModel
{
get
{
TViewModel viewModel;
bool designMode = DetectDesignMode();
 
if (designMode)
{
viewModel = new TDesignViewModel();
}
else
{
if (resolver == null)
{
throw new InvalidOperationException();
}
 
// Use the resolver implementation to resolve
// the view model requested.
viewModel = (TViewModel)resolver.Resolve(typeof(TViewModel));
}
 
viewModel.OnLocated();
return viewModel;
}
}
}

We introduced a new static method on the ViewModelFactory named InitializeResolver(). Like the other dependencies in the system we can initialize the ViewModelFactory with the proper resolver in the Composition Root.

The reason this needs to be static is because the ViewModelFactory is created in XAML as a resource so that it can be statically bound. The static method is to allow the resolver to be injected manually because the ViewModelFactory is not created by the IoC container and cannot resolve it’s dependencies.

Now we need to update the Bootstrapper.Initialize() method.

Before

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
private void Initialize()
{
container = new Container();
 
container.Register<IEventAggregator>(new EventAggregator());
container.Register<IDatabase>(new Database());
 
container.Register<MainViewModel>(c => new MainViewModel(
c.Resolve<IEventAggregator>(),
c.Resolve<IDatabase>()));
 
container.Register<BrowserViewModel>(c => new BrowserViewModel());
 
container.Register<SettingsViewModel>(c => new SettingsViewModel(
c.Resolve<IDatabase>()));
}

After

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
private void Initialize()
{
// Create the IoC container.
container = new Container();
 
// Initialize the ViewModelFactory with the Funq ViewModelResolver.
ViewModelFactory.InitializeResolver(new FunqViewModelResolver(container));
 
// Register the dependency composition.
container.Register<IEventAggregator>(new EventAggregator());
container.Register<IDatabase>(new Database());
 
container.Register<MainViewModel>(c => new MainViewModel(
c.Resolve<IEventAggregator>(),
c.Resolve<IDatabase>()));
 
container.Register<BrowserViewModel>(c => new BrowserViewModel());
 
container.Register<SettingsViewModel>(c => new SettingsViewModel(
c.Resolve<IDatabase>()));
}

This is how we use the ViewModelFactory in XAML (leaving out the less important markup). This is all created via Blend, no hand written XAML required. A designer can hook this up in a few clicks.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
<phone:PhoneApplicationPage
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:kellafeed.ViewModels">
 
<phone:PhoneApplicationPage.Resources>
<vm:MainViewModelFactory x:Key="MainViewModelFactgoryDataSource"
d:IsDataSource="True"/>
 
</phone:PhoneApplicationPage.Resources>
 
<phone:PhoneApplicationPage.DataContext>
<Binding Mode="OneWay"
Path="ViewModel"
Source="{StaticResource MainViewModelFactgoryDataSource}"/>
</phone:PhoneApplicationPage.DataContext>
 
<-- XAML visuals... -->
</phone:PhoneApplicationPage>

Here’s how that XAML is created by the designer in Blend.

image

imageimage

This is how the view looks with the DataContext set within Expression Blend. Full design time support!

image

After this 2nd iteration of refactoring we have now encapsulated the IoC container and removed it from being globally accessible and abused. This is important because we enforce a clean path rather than opening the container up and allowing people to go in a wrong direction. We do use the IoC container in the ViewModelResolver but it is a specialized implementation to solve a specific problem and does not open the door for abuse.

Our code base is even more maintainable than it was after part 1 which also raises the level of testability. Our view models were already very testable after part 1 but the ViewModelFactory is now testable without requiring an IoC container by mocking the resolvers.

We reduced a lot of coupling and increased testability quite a lot in these 2 iterative refactoring steps!

Stay tuned for more improvements!

Series

  • Barry Latimer

    Really interesting article, but one thing stuck out to me as a bad “code smell” you have ended up creating a singleton that requires initialization.

    A singleton must be responsible for its own setup otherwise people who use the BootStrapper class need to be aware of the fact it can be in two states, created but not initialized and initialized. Normally the distinction between a static class and a singleton, is that a singleton is able to construct itself appropriately.

  • http://blogs.msdn.com/gblock Glenn Block

    Great post Kelly. It’s really exciting to read your explanation and see how you are grokking the concepts. I think you nailed it with this post….

    With one exception that Barry mentioned, Bootstrapper should not be a singleton. It should be a single instance that is newed up in the application.

    Excellent job!
    Glenn

  • http://blogs.msdn.com/gblock Glenn Block

    Also, this is a great post from Nick Blumhardt that discusses the question of where / how to expose the container http://blogs.msdn.com/b/nblumhardt/archive/2008/12/27/container-managed-application-design-prelude-where-does-the-container-belong.aspx

    And for the record, Nick was the first person I heard use Composition Root :-)

  • kellabyte

    Sorry folks, I actually had the Bootstrapper not be a singleton in a revision of my code and I forgot to update my blog post draft. I just updated it.

    Thanks for calling me out on it!

    Now I must rush to work :)

  • Michael Butler

    I’m enjoying this series, but you’ve confused me ;-)

    In the IViewModelResolver

    public interface IViewModelResolver
    {
    TViewModel Resolve();
    }

    the interface is defined as above, but in the implementation the Resolve method takes a type argument. Did I skip a code snippet somewhere?

    Michael (Still learning)

    • kellabyte

      I fixed this, sorry! I forgot to update my draft to match some code changes I made while writing the blog post. Thanks for pointing it out.

      • Michael Butler

        Thanks. Thought I was missing some special C# coolness I didn’t know about. ;-)

        Looking forward to the next part, you given me some good pointers sofar.

  • ct

    Good article. What do you think about replacing DI with Verbs? http://thorstenlorenz.wordpress.com/2011/07/23/dependency-injection-is-dead-long-live-verbs/

  • http://wp7trenches.wordpress.com Derek Lakin

    Great article series; thanks very much. I’m employing the same approach on a project as I type :)

    Just a couple of typos that I spotted:
    IViewModelResolver code listing:
    * IViewModel Resolve();
    should be:
    IViewModel Resolve(Type viewModelType);

    FunqViewModelResolver code listing:
    * private Dictionary methods = null;
    should be:
    private Dictionary methods = null;

    Thanks,
    Derek.

  • wangzq

    I think this is not the right fix for anti-pattern Service Locator; take a look at Mark’s blog about using Abstract Factory to fix Service Locator.

    The problem here is that to achieve blendability we have to use default constructor, which means we have to rely on the Service Locator in a way.

  • Pingback: Windows Client Developer Roundup 078 for 8/15/2011 - Pete Brown's 10rem.net

  • http://blogs.msdn.com/gblock wangzq

    Wangzq

    AbstractFactory is a factory that creates a specific thing, and is not a generic locator. In this case the ViewModelFactory will only resolve view models, you cannot use it as a general purpose locator.

    I am not sure what you mean about default ctor, as the ViewModel itself supports ctor injection.

    As to the VIEW that does have to use a default ctor, but that is a product of wanting to have it be constructed by Blend.

    There are alternative ways to get the view to be constructed through an IoC, but those approaches won’t work in the designer.

    Interested in your thoughts on a better approach.

  • bitbonk

    It would be really cool to see the source code for the completely refactored application (or another stripped down example).

  • http://twitter.com/stevehebert Steve

    I’m curious why you would use:

    container.Register(c => new SettingsViewModel(c.Resolve()));

    When you can simply allow the container to map the constructor and do DI on its own with:

    container.RegisterType();

    This frees you from brittle code and saves the Func parameter registering for the crazy-cool features.

    Then you can start to move beyond the ‘A needs a B’ type constructors. Consider this:

    class A { A(B instance) {} } – A needs a B
    class A { A(Lazy instance) {} } – A needs a B at some point in the future
    class A { A(Func
    factory) {} } – A needs to create instance of B
    class A { A(Func factory) {} } – A needs to provide parameters to create B

    A good container will allow you to do all the above by simply registering B once – Lazy and Func should come along for the ride and be tied to the declaration of B to the container.

    http://nblumhardt.com/2010/01/the-relationship-zoo/

    Enjoy!

  • http://twitter.com/stevehebert Steve

    Hmm – looks like the markup voided the typed generic parameters above. sorry.