1

When using Dependency Injection across internal classes (implementation details) in a library which is to be consumed by multiple applications, is there a good way to configure the IOC container?

From my understanding, the canonical way of doing this in a regular program is by setting up the dependencies at the “Composition Root” such as the main method. But this approach cannot be taken for a library (consumed by applications with their own composition roots), especially if the library has a large public API with many entries points.

Is the best way to wire dependencies in this instance while still allowing unit testing (as opposed to integration testing) of the public API to use a ServiceLocator and provide some mechanism for mocking the ServiceLocator? Note that all dependencies themselves could still use constructor injection, but the top level public API would have to use a ServiceLocator to resolve the top level dependencies.

What if there is a single entry point to the library (but it would still be good to unit test that entry point), does that change the answer?

2

The best option is to have two libraries.

One would be your library, which is IoC-agnostic. All classes use constructors for their dependencies. If there are any specific ways objects need to be created, the library provides abstractions that can be implemented by IoC code. As for your comment for "cannot use same composition root" I find that hard to understand. If your code is used by the library user, then it must have came from same place.

Second would be library for providing default setups for specific IoC containers. There won't be single library, but library for each of popular IoC container. Those can be created either by you or have 3rd party developers create them for their convenience. This code will contain just IoC setup code with possible options for various use cases. The user of the library just then needs to call a single method to setup it's IoC for your library.

This approach is used by ASP.NET Core. It is made even easier that most libraries assume .NET Core's Microsoft-provided IoC containers for their default setup code.

1
  • This approach makes a lot of sense, I am actually using .NET Core's Microsoft.Extensions.DependencyInjection, so the ASP.NET Core example is good. The comment about a composition root was just that it did not exist in my library, but instead in the consumer. It didn't occur to me previously that I could piggyback onto the consumer's IoC container in this way which saves having to take on an additional dependency. – brent Nov 21 '19 at 9:31
2

You should not be using any dependency injection containers when you are writing a library meant to be consumed by others. It may be acceptable if you know all the projects using the library and they all agree to use the same DI container. This might be the case with internal company libraries.

I find it extremely impolite to force a DI container on people for some features that probably would work fine without it. Do not do Service Locators either.

As you pointed out, you should define dependencies in the constructors of classes. If you want to offer some complex tree of objects to your users, create builders that build that tree. You can have multiple builders for different parts of your library.

2
  • 2
    "Do not do Service Locators either." I'd have liked it if you'd put that in 200pt bold text, flashing red. If one is using a service locator, then one isn't doing dependency injection. Never use service locators. Other than that point not being rammed home strongly enough for my liking, your answer is spot on :) – David Arno Nov 21 '19 at 8:38
  • Thanks! Yes, I definitely wouldn't want to force a DI container on people which is why I can't take the composition root approach. Just to add some further details, the complex tree of objects wouldn't be for (or accessible by) consumers, but to gain all of the benefits of DI for the internal implementation details/development of the library. In particular, I would also want to be able to unit test the public API (which uses all of the depencies) without needing to always write integration tests. A ServiceLocator at the top level, and proper DI elsewhere seemed a way to achieve this. – brent Nov 21 '19 at 8:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.