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 ?
Best Answer
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:
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:
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 presumeDataStock
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.
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):