Using Dependency Injection in Published API's

Dependency Injection (DI) is a very useful pattern. It makes a class's dependencies obvious. If dependencies are not provided, the class can't be created. When testing, it allows the internal dependencies of a class to be swapped out for mock objects. It is a best practice when trying to write decoupled testable code.

When using the DI pattern in a codebase you would wire together all the dependencies in the application root. To avoid manually wiring up dependencies you may turn to a DI container framework such as AutoFac or Ninject. DI frameworks handle all the heavy lifting of wire-up.

At this point you may be wondering what this has to do with creating a published code API? When you consume an complex API as a user, you don't want to have to wire up all the internal details. Here is a contrived example.

// This will drive an API consumer crazy
public class TheAPIConsumersClass {
    Airport WireUpTheShinyNewAirportAPI() {
       var runwayLights = new Array<BrightLight>() { new BrightLight() }
       var radar = new RadarUnit();
       var controlTower = new ControlTower(radar);
       var airport = new Airport(airplane, controlTower);
       return airport;
    }
}

 // All your user really wants to do is this something like this...
public class TheAPIConsumersClass {
    Airport WireUpTheShinyNewAirportAPI() {
       var airport = new Airport();
    }
}

If you don't hide the wire-up details from the users, you may wish you did when they start calling with questions. There are a few things could be done here. One is to create a factory class for users to call. For a very complex dependency graph, this might be your best option. Here is another approach. Mark the constructor with dependency injection as internal. Have a public constructor that calls the internal constructor and provides the required dependencies. This way you can get the benefit of DI for testing, while still providing a simple constructor for consumers.

public class MyApi {
   public MyApi() : this(new Thing1(), new Thing2()) {  }
   internal MyApi(Thing1 thing1, Thing2 thing2) {
          _thing1 = thing1;
          _thing2 = thing2;
    }
}

Overall, when creating an API consumed by others be kind. Don't require them to do the complex dependency graph wire-up. Provide top level classes that hide details and make the library simple to use. Keep your DI, just don't make it public. In this case you can have your cake and eat it too.