Welcome to MSDN Blogs Sign in | Join | Help
Container-Managed Application Design, Prelude: Where does the Container Belong?

There is surprisingly little information out there in cyberspace on how IoC containers should fit into application architectures.

In parallel with my MEF/Ruby series, I'm going to discuss some of the principles that I believe should be applied to "container-managed" application design.

The Enigmatic Ideal

In the world of systems built using dependency injection, you hear plenty of mysterious statements like:

“Except for when bootstrapping, don’t access the container directly!”

This kind of thing caused me to scratch my head for the best part of a year before I finally caught on to what was meant.

Basic Mental Models

When confronted with the task of designing an application around an IoC container, the place that the container sits in that architecture usually seems to be influenced by the designer's mental model of what, exactly, an IoC container is.

Model 1: a Dictionary of Services

The dictionary or associative array is one of the first constructs we learn about in software engineering. It is easy to see the analogy between a dictionary and an IoC container that composes objects using dependency injection:

image

Maps to:

image

All that appears to differ is:

  • Some fancy new terms ‘register’ and ‘resolve’ are used, instead of indexer access
  • The logger is created via reflection, saving some typing and configuration code in the case of inter-dependencies

If you come onto a project with established usage of a dependency injection container and had to figure out how to write the code you need to interact with the rest of the system, chances are this is the first mental model you’ll apply.

Model 2: an Abstract new() Operator

Dependency injection containers are often praised for eliminating implementation type dependencies. Going back to our example, we’ve made our request for an ILogger independent of the implementation type that provides it (ConsoleLogger in this case.)

Web frameworks like Monorail and ASP.NET MVC use our dependency injection container to create Controller instances in a similar way.

Perhaps it makes more sense to think of the container as an abstract new() operator?

image

Becomes:

image

Here our container usage has not only abstracted away the concrete type of our MailTransport but also taken care of configuring its Host property. (Configuration must be a feature of our abstract new() operator since the configuration parameters on an instance depend on its concrete type rather than the service it provides.)

Design Outcome: the Global Container

Following either of these mental models encourages you to think about the container as something you retrieve things from. Even the name 'container' pushes us in this direction.

From this perspective, there isn't much of a challenge in the application architecture design assignment!

We'll just create a static property Container on a class, say, Global and get instances from it using Global.Container.Resolve<MyService>().

Easy!

image

This snippet should be familiar as a pattern with another name: the static Service Locator.

Problems with the Global Container as a Service Locator

There are good reasons why this pattern can rightly be called an anti-pattern:

Fragility. The container acts like a bucket of global variables. All of the robustness that is achieved by forcing components to publicly declare dependencies is lost. Unexpected dependencies can appear between seemingly unrelated parts of an application, complicating maintenance.

Reduced Composability. When dependencies are injected, the container can be configured to provide different implementations to different consumers. This is often necessary when combining components that were built independently. When dependencies are retrieved from a global container, the container does not know the identity of the requestor, and is forced to return the same implementation every time.

Limited Reuse. Where the global container resides can limit the reusability of components that depend on it. Migrating components to a WPF application will require code changes if they depend on the global container attached to the HttpApplication class.

Implementation Issues. Concurrency, re-entrancy, circular reference detection, and component lifetime management are much harder/messier in a global container based solution.

Resumption of Control: Generally speaking, these issues arise because calling directly into a global container is a resumption of control. In a container-managed application, the container is given the responsibility of getting the right instances into the right places in order for work to be done. Calling into a global container takes over some of this responsibility in a much less manageable way.

Sometimes, because of the design decisions made in many current-generation frameworks, the Global Container is a necessity, but in order to advance the state of software engineering we need to look beyond this.

Thinking Inside the Box

The mental models listed earlier should have rung some alarm bells:

The container is a dictionary of services? Hang on! If I ask for the same service twice, why do I sometimes get different instances?

Good question. It is easy to configure a dependency injection container to return a new instance of the ConsoleLogger each time one is resolved. Not very dictionary-like, is it?

So the container is really an abstract new operator? Wait! If I sometimes get the same shared instance back, when can I Dispose() it?

These two models aren't even compatible. Sometimes the container hands out new instances, but sometimes what comes back from a call to resolve is a singleton, or an instance that will be shared within some kind of context like the current transaction.

So things are now confusing: who owns these instances, and where do they ‘live’?

Revised Mental Model: An Interconnected System of Objects

Here’s the problem: look at the example on the Autofac homepage. It shows how Register() and Resolve() are used in Autofac. This is a pretty common kind of introductory example to dependency injection. Notice what’s missing?

The most important classes in this example are not ContainerBuilder or Container. From an application architecture perspective, we need to see the Straight6TwinTurbo and Car classes!

The resolve operation is just a way of finding an entry point into a self-contained system.

The entire application resides within the container. There is no ‘outside’ except for whatever entry point gets the ball rolling. An understanding of IoC from this perspective does not revolve around its external APIs like Register() and Resolve().

image

Using this model, the container to gets component instances to the right place at the right time, and application design re-focuses on the implementation of the components themselves.

Design Outcome: Injected Context

This new perspective on the world leaves us with a challenge when implementing our revised IControllerProvider. This service needs to return (potentially) new, parameterised instances of the Controller type.

The solution, commonly implemented in applications today, is to allow the container to provide context to our controller provider:

image

We assume here that ControllerProvider is itself hosted within the container. IContext is an interface that Autofac automatically provides to any components that require it. You can create and use an equivalent interface in any other popular IoC container (there's a MEF example linked from below.)

IContext provides the instance resolution features of the container to the application, but in a controlled manner. The object implementing IContext provides the container's services, but may not be the container itself.

Because this pattern gives the container or application developer an opportunity to customise or proxy the IContext implementation handed out to any particular component, most of the problems associated with a Global Container are mitigated.

Container-Managed Application Design

If you feel like this article and its explanations are somewhat incomplete, you're right. As the title of this post suggests, it is an introduction to a series on the topic. I don't have all the answers, and I don't even have all the questions, so the installments may come in fits and starts. Nevertheless, if you're out to design robust architectures that make use of an component-based composition technology there should be something of interest for you.

I hope this article has whet your appetite!

Sample Code

The sample code, based on MEF preview 3, is very, very rough, and is really only worth consulting if you need to step through some running code in order to clarify the concepts in this article.

Published Saturday, December 27, 2008 12:32 AM by niblumha

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Friday, December 26, 2008 8:33 PM

It certainly wet mine Nick. This is a great post, in that it explores a topic which is often not discussed after you get past the "Should I use an IoC" or not.

I like the approach of delivering a context class through IoC. So you would suggest a more granular object as opposed to individual factories/resolvers?

Recently I took a look at how in Prism we injected the container into container in order to accomodate dynamically resolving new instances from within a controller. Based on our discussion I started prototyping around an IResolver<T> interface. This instead of passing the container in the constructor, you pass specific resolver instances, thus benefitting from composition, and improving intent.

I can see how the context approach could have been used as well i.e. create a specific context that supplies the services that the component needs.

The downside I guess is that it will require desigining more context classes, and adding more types to the system.

Glenn

Glenn Block

# Container-managed applications @ Friday, December 26, 2008 8:51 PM

OK, so you've past the point of deciding whether or not you will use an IoC container. Then you find

My Technobabble

# Container-managed applications @ Friday, December 26, 2008 8:52 PM

OK, so you&#39;ve past the point of deciding whether or not you will use an IoC container. Then you find

Glenn Block

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Friday, December 26, 2008 10:13 PM

At this point, don't you end up back at Service Locator? Granted, its a local one rather than global.

Chris Tavares

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Saturday, December 27, 2008 7:53 AM

This article series is great.  Too many examples of IoC are just that, examples.  They don't show big picture samples; I'd love to see an example of hosting the container in a pure ASP.NET Webforms application, and how it would then be used in both Pages and in library classes that the webforms app depends on.

Brett J.

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Saturday, December 27, 2008 12:53 PM

@Glenn & @Brett, thanks for dropping by! :)

@Chris:

As far as the 'intentionality' of IContext goes, yes, it is something of a Service Locator (I'll address this in the next post, I hope.)

The key to the difference is, as you said, the fact that it is local to the component that uses it.

The properties of the end result turn out to be very different for the same reasons that other components benefit by switching from 'traditional' dependency coupling to Dependency Injection:

* it is easily testable - the IContext parameter can be mocked

* it publishes its dependency on the context - no nasty surprises

Having a injected/local context object rather than a global one opens up a lot of possibilities - the isolation supports security or multi-tenancy, for example, through 'decorated' context objects.

These kinds of thing can be achieved with a global container, but the composability of the solution suffers:

Using Injected Context, things like a 'multi-tenancy decorator' can be constructed in a modular way, and 'configured into' the system like any other IoC-driven component.

Using a global Service Locator, these kinds of things end up hard-wired into the application host itself, reducing the amount of application code that can benefit from IoC.

Hope this covers your question, this comment is starting to look like a post of its own :)

niblumha

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Saturday, December 27, 2008 4:06 PM

Rather than passing in IContext which feels too "container-y", we typically pass in a <a href="http://code.google.com/p/autofac/wiki/DelegateFactories">delegate factory</a>.  It adds a small layer of indirection—and complexity—but ultimately calls back through the same IContext without having an explicit dependency on IContext.  In this manner Autofac, or any other IoC container, need not be a project dependency—the IoC container can be part of a separate configuration project if desired.

Could you also address a few nested container examples in your upcoming posts in this series?  I'm specifically interested in how to properly work with nested containers—without calling them from domain code—to better facilitate unit of work patterns.

It's great to see you blogging on dependency injection again.  It's been a while!

Jonathan

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Saturday, December 27, 2008 6:28 PM

Thanks Nick. This article is a great primer.

For the longest time I have had trouble articulating the 'why' when I introduced IoC to a new project.

I think that once you 'get' the benefits, they become so blindingly obvious to you that they become almost impossible to explain coherently to others who don't 'get it' yet.

Steve Burman

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Saturday, December 27, 2008 8:05 PM

@Jonathan You're one step ahead ;) ... Thanks for the suggestions.

@Steve I know what you mean!

niblumha

# How IoC containers fit in an application architecture. @ Sunday, December 28, 2008 1:25 AM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

DotNetKicks.com

# Container-managed applications @ Monday, December 29, 2008 3:40 AM

OK, so you&#39;ve past the point of deciding whether or not you will use an IoC container. Then you find

Community Blogs

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Monday, December 29, 2008 12:51 PM

Where the heck was this blog post 5 months ago when I was trying to teach myself to use IoC and faced with these same problems?

I went through all the steps that you describe and refactored my application something like 20 times.

Great idea for an article.  I will certainly be watching this blog.

Togakangaroo

# Declarative Context Adapters @ Sunday, January 04, 2009 11:59 AM

The first post in this series introduced the problem of accessing IoC container features dynamically.

Nicholas Blumhardt

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Wednesday, January 07, 2009 4:58 PM

Does this work? http://mdpopescu.blogspot.com/2009/01/servicelocator-anti-pattern.html

Marcel Popescu

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Wednesday, January 07, 2009 4:59 PM

Finally... for some reason the full comment was ignored both times I tried to submit it.

Marcel Popescu

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Friday, January 23, 2009 5:00 PM

I was fascinated by your idea.. until I tried to figure out how to apply it to my project. In short, I can't see how the entire application can reside inside the container if it consists of a framework and some client code that utilizes this framework.

ulu

# re: Container-Managed Application Design, Prelude: Where does the Container Belong? @ Friday, February 06, 2009 2:33 AM

This is a very welcome post.  The team I'm working on is building a large (for me, anyways) application using Unity for the first time.  It will be nice to read some blog posts exploring larger architectural concepts about DI that are deeper than just "look ma, its a new() replacement".

+1 to Jonathan's request on nested/child containers.

Jeremy Wiebe

Leave a Comment

(required) 
required 
(required) 
Page view tracker