3

If I understand it correctly, in classic 3-tier/n-tier architecture the goal is to ultimately separate responsibilities in such a way that each layer shouldn't have to know about what is going on/being used internally in lower tiers.

However, if the objects in each tier (especially business) are structured to be testable, their dependencies are defined as part of their public contracts (when testing a 2nd-tier object with a 3rd-tier object dependency, mock/stub out the 3rd-tier object and provide it to the 2nd tier object). This means that at implementation time, the first tier is responsible for grabbing a third-tier dependency for use in constructing a 2nd-tier object. I am not opposed to this if it's a very bland object but if it is a data access component that requires, for example, a connection string, the first tier should not be responsible for that. Apart from that, the more dependencies you have, the more the top tier is responsible for instantiating and passing in all of the dependencies of each object in the slice of the onion it's using.

The only way I've ever seen this problem subverted is through the use of IoC, but I'm working in a situation where that option is off the table. Are there any other ways to structure the code to be testable but not face the problem of instantiating/providing dependencies for each tier in the top tier? I should mention I am working in a web app.

(I've gone over this post as a refresher on "the rules.")

EDIT: I think I can sum up the problem as such: without using some kind of IoC container or bootstrapper, is there a way to structure the code to be testable that doesn't violate the dependency depth principle, which is that every layer in the onion can only reference the layer below it?

moarboilerplate
  • 1,633
  • 9
  • 23

2 Answers2

1

It is not about a top tier itself but about bootstraper which will initialize your application. Depending on the rest of the architecture it can either be responsible for launching entry point in top tier of your application or it can simply be part of top tier initialization (that is even used with IoC frameworks).

Example in .NET:

If you are building standalone application you can initialize stuff as part of main execution path and only when everything is initialized you will launch your entry point. In case of web application or web services such bootstraper usually takes place in application start handler and your tiers are used when handing HTTP requests.

Btw. Question should be about IoC container. Not about IoC itself. IoC is the approach to control inner logic from outside - that is achieved by injecting dependencies. It is main approach for easily testable applications. IoC container is part of a framework which builds dependency hierarchies for you.

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • Thanks for the reply. Since I am building only one set of pages in a legacy web app, I am looking for something non-intrusive that wouldn't have to be applied site-wide which is where the team's hesitation to implement a container comes from. I could apply the bootstrapper/container to one set of pages, but doing so would imply an architectural decision that would put the onus on the rest of the team to follow. I just don't think we're ready to make such a universal change. – moarboilerplate Jul 25 '14 at 16:48
  • I also edited the question to clarify I am talking about an IoC container instead of the general IoC concept. Thanks. – moarboilerplate Jul 25 '14 at 16:50
  • Isn't the only architectural change initialization of container when application starts? Most of your objects in web application will be created per request anyway and only for pages where you ask for that. – Ladislav Mrnka Jul 25 '14 at 16:53
  • +1. I would add that the bootstrapper will (likely) need to compose the full dependency graph, and will thus need knowledge of all the assemblies that need to be wired up. This is actually true of using an IOC container as well, but the container can do this via late binding, making hard references to other assemblies optional. – Phil Sandler Jul 25 '14 at 16:55
  • Actually, late binding is possible without a container, but it's much more of a PITA. – Phil Sandler Jul 25 '14 at 16:56
  • If I don't use a bootstrapper, in which case instead of rolling it myself I would use an IoC container, when the code is structured to follow dependency inversion principles, I require references to deeply-nested dependencies in order to get an instance and provide it as a dependency to another object. This seems to violate the entire point of n-tier architecture because I need to reference all this stuff and compose it in the codebehind of a web page. Is there a way around this without abstracting the whole object graph out to a bootstrapper/container? – moarboilerplate Jul 29 '14 at 14:05
  • I should mention that one of the constraints of the environment is that I am forced to use the container as a service locator (only at the very top level of dependency resolution) as injecting WebForms is out of scope. – moarboilerplate Jul 29 '14 at 14:13
0
  • You can invert control without an IoC container, as @LadislavMrnka explained.

  • You can event do Dependency Inversion and benefit from loose coupling and testability without doing interface-based IoC. Events and higher-order functions are two ways to do that.

  • You can decouple part of your code base, no need to do it all at once. An object can have its dependencies decided and/or injected by the object that consumes it, you don't have to defer it all the way down to a bootstrapper.

Considering this, the answer to your question is "yes" and in many different ways :)

I think it's a good idea indeed to start small, making a few components testable (and tested) at first rather than trying to testable-ify and IoC-ify everything up front in one long, tedious and risky refactoring. IoC can come later, when most of your code base has been tested and sanitized.

Community
  • 1
  • 1
guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • The main difficulty I run into is that when structuring dependency inversion in a WebForms web app where I'm not injecting the forms or using a crazy httphandler, the codebehind becomes the place where depending on my implementation, I have to instantiate the dependencies for everything several tiers down, or if following the higher-order function approach, call the methods. This leads to my codebehind requiring a reference to things that should be encapsulated in a separate library (even if I use abstract factory, I still have to know the type of the object that will be output). – moarboilerplate Jul 29 '14 at 14:02