Dependency Injection (DI) internally in reusable class library
Asked Answered
J

0

6

I have a set of projects which provide all the functionality necessary to run a .Net service. The project makes use of Dependency Inject via Ninject.

My (simplified) solution looks like this:

Project 1: Windows Service (Composition Root)
^
Project 2: Server Engine & Heavy Lifting (Ninject Module)
^
Project 3: Persistence / DAL (Ninject Module)
^ 
Project 4: Interfaces & Basic / Shared functionality (Interfaces)

After some significant development, turns out I will need to create a new "client" library project used to communicate with the Windows Service. This client becomes a new project (Project 5) and depends on the functionality in Project 3 and Project 4. I.e.:

Project 5: Client (Class Library)
^
Project 3: Persistence / DAL (Ninject Module)
^ 
Project 4: Interfaces & Basic / Shared functionality (Interfaces)

3 Immediate Problems:

  1. I don't want to put the responsibility of wiring up an object graph on the consumer of the Client. The consumer should just use it like you have any other library you've used. You shouldn't have to worry about the internals of the library to make it work.
  2. The Client is a class library and doesn't have an entry point of its own to consider my composition root and establish the object graph.
  3. Even if I did have an entry point in the Client, my reading suggests I shouldn't have library dependencies on a DI framework "In that case [of creating a reusable library] you should typically make your library working without a DI container. You should yourself not take a dependency on such a container, because this would drag the container in" (from Locate the correct composition root for a .NET library). Another excerpt from a different post reads: "Only applications should have Composition Roots. Libraries and frameworks shouldn't." (from http://blog.ploeh.dk/2011/07/28/CompositionRoot/).

These problems root from the fact that I have already built much functionality into Project 3 and Project 4 that is mandatory to be used in the Client, yet these 2 projects already have a dependency on Ninject, so by proxy, the client also has a dependency on Ninject.

I can easily solve this problem using various methods, but I haven't found a resouding solution, they all feel "hacky", like I'm shoving a square peg in a round hole (What are the best practices for class libraries using dependency injection for internal operations?).

Jame answered 2/12, 2015 at 13:25 Comment(10)
Did you read this? blog.ploeh.dk/2014/05/19/di-friendly-librarySivie
Hi @Steven, I just gave it a read. While I understand the principals there, they are all centering around the concept of the application wiring up dependencies on behalf of (in my case) the Client and thus has too much of a heavy hand in using the Client IMO. My client is simple, it is not a massive framework, it should be easy to use with a few lines of code and should absolutely not require the consumer to wire up a graph.Jame
@RyanGriffith That article suggests that the library can provide a Facade in order to make usage from a client as simple as possible. Using such a Facade could literally be a one-liner, depending on the preconditions of your library. Can it get any easier than that?Sergei
"nor should I even be using DI within my library." Where did you read that?Sergei
@MarkSeemann, thank you for the great posts, they're very helpful. I have corrected my question to correctly attribute that statement you mention. Regarding a Facade, would that become the composition root within the Client where we'd hookup our dependency graph? To reiterate, I'm bound to wiring up dependencies and using a specific DI framework because of my shared projects.Jame
Regarding Facades, is there anything in the article's example that's not sufficiently clear?Sergei
The example is clear and very informative, however, the advice is not when it comes to my problem. In your article(s), you take a general stance of the composition root being within the application and specifically outside of the actual library. I can solve all my problems by making my composition root inside of my facade in Client, but 1. Your example doesn't show this and 2. you appear to suggest against it. You obviously have a lot of credibility, so I'm cautious to mix the two concepts when your posts appear to suggest against it. Please set me straight if I'm misinterpreting something.Jame
How about using a DI container internally in your library but not exposing it to the outside world? Offer the facade to the outside world so a consumer of the library can easily use it, either by configuring his own DI container or not using a DI container at all. You could also ILMerge the DI container into your assembly so the consumer of your library does not have to manage more dependencies.Sisk
Hi @MarkSeemann, just following up on this with my last comment. If you have a moment, I'd appreciate your thoughts.Jame
@RyanGriffith It looks like you already found the answer to that question: https://mcmap.net/q/831274/-what-are-the-best-practices-for-class-libraries-using-dependency-injection-for-internal-operations-duplicateSergei

© 2022 - 2024 — McMap. All rights reserved.