Mare Infinitus
Mare Infinitus

Reputation: 8182

How to use dependency injection in enterprise projects

Imagine you have an application with several hundreds of classes implementing dozens of "high level" interfaces (meaning component level). Is there a recommend way of doing dependecy injection (like with unity). Should there be a "general container" that can be used for bootstrapping, accessible as a Singleton? Should a container be passed around, where all instances can RegisterInstance? Should everything be done by RegisterType somewhere in the startup? How can the container be made accessible when needed. Constructor injection seems false, controverse of being the standard way, you have to pass down interfaces from a component level to the very down where it is used right on startup, or a reference is hold ending up in a "know where you live" antipattern. And: having a container "available" may bring developers to the idea of resolving server components in client context. how to avoid that?

Any discussion welcome!


edit for clarification

I figured out a somewhat realworld example to have a better picture of what problems i see.

lets imagine the application is a hifi system. the system has cd player (integrated in a cd-rack) and an usb port (integrated in an usb rack) to play music from.

now, the cd player and the usb port shall be able to play mp3 music.

i have an mp3 decoder somewhere around, which is injectable.

now i start the hifi system. there is no cd inserted yet and no usb stick pluged in. i do not need a mp3 decoder now.

but with constructor injection, i have to already inject the dependency into the cd rack and the usb rack.

what if i never insert a mp3 cd or an mp3 usb stick?

shall i hold an reference in the cd rack, so when an mp3 cd is inserted, i have a decorder on hand? (seems wrong to me)

the decoder is needed in a subsystem of the cd rack, which is only started if a mp3 gets inserted. now i have no container in the cd rack, what about constructor injection here?

Upvotes: 3

Views: 2859

Answers (4)

Steven
Steven

Reputation: 172696

Should a container be passed around

No, since this leads to the Service Locator anti-pattern.

where all instances can RegisterInstance

Services should be registered in the start-up path of the application, not by types themselves. When you have multiple applications (such as web app, web service, WPF client), there will often be a common bootstrapper project that wires all services together for shared layers (but each application will still have its unique wiring, since no application behaves the same).

Should everything be done by RegisterType somewhere in the startup

Yes, you should wire everything up at start-up.

How can the container be made accessible when needed.

You shouldn't. The application should be oblivious to the use of a container (if any container is used, since this is optional). If you don't do this, you will make a lot of things much harder, such as testing. You can however, inject the container in types that are defined in the startup path of the application (a.k.a. the Composition Root). This way the application keeps clean from knowing anything about the container.

Constructor injection seems false, controverse of being the standard way

Constructor injection is the prefered way of injecting dependencies. However, it can be challanging to refactor an existing application towards constructor injection. In **rare* circumstances where constructor injection doesn't work, you can revert to property injection, or when it is impossible to build up the complete object graph, you can inject a factory. When the factory implementation is part of the composition root, you can let it depend on the container.

A pattern I found very useful, that can be built on top of the Dependency Injection pattern and the SOLID design principles, is the command / handler pattern. I found this a useful pattern in smaller apps, but it will shine when applications get big, such as enterprise applications.

Upvotes: 2

guillaume31
guillaume31

Reputation: 14064

now i start the hifi system. there is no cd inserted yet and no usb stick pluged in. i do not need a mp3 decoder now.

This seems like a perfect fit for Setter injection (=property injection in C#). Use a NeutralDecoder or NullDecoder as a default and inject an Mp3Decoder when you need it. You can do it by hand or using a DI container and conditional/late binding.

http://blog.springsource.com/2007/07/11/setter-injection-versus-constructor-injection-and-the-use-of-required/

We usually advise people to use constructor injection for all mandatory collaborators and setter injection for all other properties.

Upvotes: 1

Jupaol
Jupaol

Reputation: 21365

First of all, Dependency Injection is a design pattern that does not require a container. The DI pattern states:

Dependency injection is a software design pattern that allows a choice of component to be made at run-time rather than compile time

Take for example Guice (java dependency injection framework), in Java Guice is a DI framework but it is not a container itself.

Most of the DI tools in .Net are actually containers, so you need to populate the container in order to be able to inject the dependency

I do not like the idea to have to register every component every time in a container, I simply hate that. There are several tools that help you auto register components based on conventions, I do not use Unity, but I can point you for example to Ninject or AutoFac

I am actually writing a small utility to auto register components based on conventions using practically any DI tool, it is still in dev phase

About your questions:

Should there be a "general container" that can be used for bootstrapping, accessible as a Singleton?

The answer is yes, (there's a tool to abstract the DI tool used, it is called ServiceLocator) that's how DI tools work, there is a static container available to the application, however, it is not recommended to use it inside the domain objects to create instances, that's considered an anti-pattern

BTW I have found this tool really useful to register components at runtime:

http://bootstrapper.codeplex.com/

Should a container be passed around, where all instances can RegisterInstance?

No. that would violate the law of Demeter. If you decide to use a container is better to register the components when the application starts

How can the container be made accessible when needed

Well using the Common Service Locator you could use it anywhere in your application, but like I said, it is not recommended to use it inside the domain objects to create the required instances, instead, inject the objects in the constructor of the object and let the DI tool to automatically inject the correct instance.

Now based on this:

Constructor injection seems false, controverse of being the standard way, you have to pass down interfaces from a component level to the very down where it is used right on startup, or a reference is hold ending up in a "know where you live" antipattern

Makes me think that you are not writing unit tests heavily for your application which is bad. So my suggestion is, before choosing between which DI tool you are going to use, or before taking all the answers you get to this question into consideration, refer to the following links which are focus on one thing: Write clean testable code, this is by far the best source you could get to answer yourself your own question

Clean Code talks:

Articles

The following links are super highly recommended

http://misko.hevery.com/2008/09/30/to-new-or-not-to-new/

http://www.loosecouplings.com/2011/01/how-to-write-testable-code-overview.html

Upvotes: 6

Wiktor Zychla
Wiktor Zychla

Reputation: 48250

First, there is the Composition Root pattern, you set up your dependencies as soon as possible, Main() in desktop, global.asax/app_start in web. Then, rely on constructor injection as this makes your dependencies clear.

However, you still need something to actually RESOLVE dependencies. There are at least two approaches known to me.

First one consist in using the Service Locator (which is almost equal to making the container a singleton). This is considered an antipattern for a lot of people, however it just works great. Whenever you need a service, you ask your container for it:

 ... business code...
 var service = ServiceLocator.Current.GetInstance<IMyService>();
 service.Foo();  

Service Locator just uses a container you set up in the Composition Root.

Another approach consist in relying on object factories available as singletons with the container injected into them:

 var service = IMyServiceFactory.Instance.CreateService();

The trick is that implementation of the factory uses the container internally to resolve the service. However, this approach, with an additional factory layer, makes your business code independent and unaware of the IoC! You are free to completely redesign the factories to not to use IoC internally and still maintain every single line of business code.

In other words, it's just a trick then to hide the container/locator behind a factory layer.

While tempted to use the former approach (rely directly on the locator) I prefer the latter. It just feels cleaner.

I wonder if there are other viable alternatives here.

Upvotes: 2

Related Questions