6

I am refactoring and introducing unit tests in a large application.

It's currently a collection of static classes with static methods that return data, like such:

// in data access project
public static class DataStock {
     public static Stock GetStock(int operation) { return my data; }
}

// in business layer project
public static class Stock {
     public static Stock GetStock(int operation) {
         var stock = DataStock.GetStock(operation);
         // do something with the data
         return stock;
     }
}

// in forms project
public class FrmDisplayStock {
     public void LoadStock() {
         var stock = Stock.GetStock(this.selectorOperation);
         // display the stock
     }
}

I am redesigning the app like this:

// in data access project
public class DataStock : IDataStock {
     public Stock GetStock(int operation) { return my data; }
}

// in business layer project
public class Stock : IStock {
     private IDataStock data;

     public Stock() { this.data = new DataStock(); }
     public Stock(IDataStock data) { this.data = data; }

     public Stock GetStock(int operation) {
         var stock = data.GetStock(operation);
         // do something with the data
         return stock;
     }
}

// in forms project
public class FrmDisplayStock {
     private IStock stockbll;

     public FrmDisplayStock() { this.stockbll = new Stock(); }
     public FrmDisplayStock(IStock stockbll) { this.stockbll = stockbll; }

     public void LoadStock() {
         var stock = stockbll.GetStock(this.selectorOperation);
         // display the stock
     }
}

This allows me to write tests for the business and forms layers, mocking the underlying layer (I am not yet testing the data access layer, but it's both more complicated to test and less interesting).

I would like to use a IoC container so that I don't have to manually wire the interfaces to concrete types every time; with deep nesting and many tests to write, it's becoming quite annoying and error-prone.

The problem is that I don't grasp how to implement it, technically.

  • Shall I write a bootstrapper for each project that wires the underlying layer ?
  • Shall I change my constructors to get the container's instance like such ? public Stock() { this.data = myContainer.GetInstance<IDataStock>(); }
  • Is it working differently than I am assuming ?
11

You want all dependencies to be injected from the absolute top layer, so you should have a dependency graph with a root, and the root has a reference to the concrete implementation of the deps at every node, registers that concretion for the interface in a container, and then grabs just the first level deps it needs from the container. But in doing so, that layer has deps the container constructs which has deps the container constructs...

Think of your dependency graph being constructed by the container like this:

public void main()
{
    var frmDisplayStock = new FrmDisplayStock(
      new Stock(
        new DataStock()
      )
    );
}

Except instead of you manually constructing each dependency at each layer of the graph, you just register the types with a container (or however the container you choose requires you to register them). For example:

public void main()
{
    var container = new SomeIocContainer();
    container.Register<IDataStock>(typeof(DataStock));
    container.Register<IStock>(typeof(Stock));
    container.Register<IDataStock>(typeof(DataStock));
    var frmDisplayStock = container.Get<FrmDisplayStock>();
}

In the above example, the final .Get call is going to use the types it knows about, and the container will presumably figure out what types are required to fulfill the whole dependency graph, identically so what was done in our previous example without a container.


So in conclusion, you want to go to the highest point in your stack you can and let it decide all the concretions. You don't want to have those default public constructors that know the concretions, because that causes tightly-coupled references between things when an interface would more than suffice. You never know when you'll want to use an IDataStock that works off a high performance cache, or 3rd party webservice, or local file, so don't presume DataStock is the only implementation you'll use. The key point of making this decision at the top layer is, if you do it below that, you'll find some layer of dependencies above that point and now you'll what- reference up the stack to define it?

Dependencies should be a directed graph with no loops (DAG) and a single root (tree), in a tree you never let peers know about each other, and you never let children know their parents. So where but at the root can you reference all dependencies without breaking the rules of a tree?


Another larger example that may make this all clearer since your example merely has 2 dependencies, not much of a sample for illustrating a concept.

public void Main()
{
    var myWidgetsService = new WidgetService(
      new WidgetEventsProcessor(
        new MessageQueueSubscriber(ConfigurationManager.AppSettings["MQConnectionString"])
      ),
      new WidgetInProcessCacheStore(),
      new WidgetSerializerService(
        new JsonSerializer()
      ),
      new WidgetAccessControlService(
        new AccessRightsProcessor(
          new AccessRightsServiceClient(ConfigurationManager.AppSettings["AccessRightsServiceUri"])
        ),
        new AuthenticationProcessor(
          new WindowsAuthenticationChecker(ConfigurationManager.AppSettings["DomainController"]),
          new EMailClient(ConfigurationManager.AppSettings["SMTPService"]) // for auth failure warnings
        )
      )
    );
}

Given the above dependencies tree, you would just make the registrations, and let the container manage the construction (totally made-up IoC API, not a real or suggested IoC framework API):

public void Main()
{
    var container = new SomeIocContainer();
    container.Register<IWindowsAuthenticationChecker>(typeof(WindowsAuthenticationChecker), ConfigurationManager.AppSettings["DomainController"]);
    container.Register<IEMailClient>(typeof(EMailClient), ConfigurationManager.AppSettings["SMTPService"]);
    container.Register<IAuthenticationProcessor>(typeof(AuthenticationProcessor));
    container.Register<IAccessRightsServiceClient>(typeof(AccessRightsServiceClient), ConfigurationManager.AppSettings["AccessRightsServiceUri"]);
    container.Register<IAccessRightsProcessor>(typeof(AccessRightsProcessor));
    container.Register<IWidgetAccessControlService>(typeof(WidgetAccessControlService));
    container.Register<IJsonSerializer>(typeof(JsonSerializer));
    container.Register<IWidgetSerializerService>(typeof(WidgetSerializerService));
    container.Register<IWidgetStore>(typeof(WidgetInProcessCacheStore));
    container.Register<IWidgetSerializerService>(typeof(WidgetSerializerService));
    container.Register<IMessageQueueSubscriber>(typeof(MessageQueueSubscriber), ConfigurationManager.AppSettings["MQConnectionString"]));
    container.Register<IWidgetEventsProcessor>(typeof(WidgetEventsProcessor));

    var myWidgetsService = container.Get<WidgetService>();
}
4
  • 1
    Thanks! So basically, I'm replacing the tight coupling to my implementations with tight coupling to an IoC library, correct?
    – thomasb
    Jun 30 '15 at 14:08
  • 1
    @cosmo0 no. You're replacing it with tight coupling to an interface - which isn't tight coupling at all. The root is the only place with tight coupling, but that point in the process is where the "process" that's going to run is defined, so it's scope is basically everything that process should do anyway. If the scope of one assembly is "Handle account related data retrieval from DB", it should not reference any UI forms assemblies. But an assembly whose scope is "Load everything into a process and execute the process", can and will reference everything. Ergo, that's the root. Jun 30 '15 at 14:49
  • @cosmo0 note, the only part of the system that references the container is the root. Everywhere else in the dependency graph, constructors take interfaces for their dependencies, and have no idea or care whether they're constructed manually (like my first example), or by a container (like my second example). All they know is they need an IBla at construction, and someone gives it to them. Jun 30 '15 at 14:53
  • Ok, I think I'm starting to understand. I still have questions, but they're more related to winforms-specific implementation, so I'll dig around a bit and come back later. Thanks!
    – thomasb
    Jun 30 '15 at 15:14

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.