3

I've been reading a lot about dependency injection and the service locator (anti-?) pattern - a lot of it on StackOverflow (thanks guys :). I have a question about how this pattern works when it's within a n-layer architecture.

I've seen a lot of blog posts where they describe injecting a IDataAccess component into the business objects. E.g.

public class Address
{
    IDataAccess _dataAccess;
    public Address(IDataAccess dataAccess)
    {
        this._dataAccess = dataAccess;
    }
}

However, I was under the impression that in an n-layer architecture, the UI layer should not need to have any knowledge of the data access layer... or even know that there /is/ a data access layer! If DI requires exposing the IDataAccess interface in the constructors of the BusinessObjects, this then exposes to the UI the fact that the Business Layer uses a data access layer under the hood - something the UI doesn't need to know or care about surely?

So, my fundamental question is: Does DI require that I expose all my lower layer interfaces to all upper layers and is this a good or a bad thing?

Thanks

Edit: To clarify (after a few comments), I know my business object should be ignorant of the which specific implementation of which IDataAccess it uses (hence the Dependency being injected in the constructor) but I thought that the layers above the BO should not know that the Business Object even requires a dependency on a DAL.

Colin Smith
  • 81
  • 1
  • 6
  • I don't really understand why you'd need to pass the data access layer of any kind into an `Address` class. The 'gluing' process between the data and the view should be done in the hosting controller/view model (depending on the architecture), which means you could map your data to the business object in above mentioned controller/view model. – Patryk Ćwiek Jul 29 '13 at 09:01
  • How exactly do you suppose the business layer gets its data? It must call *something*, and that something is what you inject. Otherwise, the business layer will have nothing to work with. Unless of course you don't actually mean a business layer and are referring to things like POCO's, which are supposed to be data agnostic, but are not themselves the business layer. – Erik Funkenbusch Jul 29 '13 at 09:02
  • I really need to blog about this because it is such a common question.. perhaps if you look up "Composition Root" you will get some better examples of a DI graph being built and how dependencies travel downwards. – Simon Whitehead Jul 29 '13 at 09:03
  • 1
    You might find this article interesting: http://blog.ploeh.dk/2012/02/09/IsLayeringWorththeMapping/ – Steven Jul 29 '13 at 09:47
  • Related: http://stackoverflow.com/questions/9501604/ioc-di-why-do-i-have-to-reference-all-layers-assemblies-in-entry-application – Steven Jul 29 '13 at 09:48
  • @Mystere Man: In my particular case, my BOs are not simply POCOs, they take care of loading, logging, caching, validation, authorisation, persistence etc. Some of these responsibilities require dependencies (e.g. on an IDataAccess) but these dependencies normally would not be exposed to the layers above right? If the IDataAccess (and ILogger etc.) are to be injected into the BO, then when someone in the UI types "new Address(", they will see the IDataAccess dependency. – Colin Smith Jul 29 '13 at 10:40
  • 1
    @ColinSmith - This is an old argument, and has been rehashed many times. Yes, technically your UI has a dependency on your data layer because your UI becomes the "Composition root", but by limiting this to ONLY the configuration section of your DI you are making this very minimal. It's one of those necessary evils. – Erik Funkenbusch Jul 29 '13 at 14:40
  • You may find @mark.seemann has a better take on this. – Erik Funkenbusch Jul 29 '13 at 14:45

3 Answers3

2

This is really a fairly complex topic, and there are many ways of doing an n-tier architecture. No one way is "the right way", and how you do it depends on your needs as much as it does your personal preferences.

Dependency Injection is about managing dependencies. If your object should be unaware of any dependency, then you would not write your objet in the way you mentioned. You would instead have some other service or method that would populate the data in an agnostic way. Data doesn't mean "Database" either. So IDataAccess could mean it comes from a database, or it comes from a network socket or it comes from a file on disk. The whole point here is that Address does not choose what dependencies it creates. This is done through configuration at the composition root.

Things need data, otherwise your app is probably useless. Making your Address object load itself, however, may not be the best way to go about things. A better approach may be with a factory class or service method.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • Even if I used a Factory pattern, I'd still have to pass the IDataAccess component to the Factory - which means that the AddressFactory would have to have a constructor that takes an IDataAccess. This just means that my UI "sees" the DataAccess layer when it constructs the Factory instead of the Business Object. I'm asking whether the UI should **ever** see the IDataAccess interface? – Colin Smith Jul 29 '13 at 09:29
  • @ColinSmith When using MvvM for example; you have your business logic layer that should know about DataAccess. Actually what you are referring to is SoC (Seperation of Concern). – Myrtle Jul 29 '13 at 09:37
  • Use a bootstrapper project that the UI references. This project can reference every other layer in isolation.. allowing you to setup dependencies. – Simon Whitehead Jul 29 '13 at 09:40
  • @ColinSmith: If you don't want your UI to communicate directly with the `IDataAccess` interface from your DAL layer, the answer is to place an abstraction in between. This abstraction would be part of your BL and the given implementation will forward the call to the DAL. But when that BL implementation only forwards and maps data it gets back, you should really question whether [layering is worth the mapping](http://blog.ploeh.dk/2012/02/09/IsLayeringWorththeMapping/). – Steven Jul 29 '13 at 09:53
  • @Steven: My Address Business object does much more than ferry data back and forth to the database. E.g. it protects the AddressID property so that it cannot be set by the UI. The BOs will all be proper domain objects but they will often need to use a DAL to persist information to a storage medium. They don't care /how/ this is done though (hence they just need an IDataAccess interface and don't rely on a SqlDataAccess class). But if the IDataAccess is a parameter in the constructor for DI purposes, this then exposes the fact that I am using **a** data access layer to the UI. – Colin Smith Jul 29 '13 at 10:30
  • @Steven: I read the article you linked to and, personally, I firmly come down on the side of layering over having a monolithic application. I have a lot of experience with both types of application and I've found that the mapping pain is analogous to a constant, low-level ache... whereas the maintenance pain of a monolithic application just keeps growing worse and worse as the application grows! – Colin Smith Jul 29 '13 at 10:33
1

I think the answer is rather simple. Your bottom layers (interface, bll, dal, entities) are just a bunch of libraries. It is up to the client to decide which libraries to be used and it will increase client's flexibility. Moreover they are libraries, so any application-related configurations (connection strings, data caching, etc) lies on the client. Those configuration itself, sometimes also need to be injected and included into Composition Root.

However, if you want to has an uniform logic and not client's flexibility, you can choose web/app services as an additional layer.

1st Layer        Entities

2nd Layer       Interface

3rd Layer       BLL  &  DAL

4th Layer    Web/App Services

5th Layer           UI

This way, your composition root exists in one layer (4th). And add your UI just need to add service reference to 4th layer (or 1st if needed). However, this implies the same Mark Seeman's article again, layering is worth the mapping. I assume that you can change the app/web service to Composition Root.

Moreover, this (app/web service) design has pros/cons. Pros:

  1. Your app is encapsulated

    Your app is being bridged by app/web services. It is guranteed that your UI don't know the DataAccess, thus fulfill your requirements.

  2. Your app is secured

    Simply said, having UI need to access app service is a huge gain in security aspect.

  3. Access Portability

    Now your app can be accessed everywhere. It can be connected by 3rd party app (other web) without has relying on dlls.

Cons:

  1. Overhead cost during service call

    Authentication, network connection, etc, will cause overhead during webservice call. I'm inexperienced for the performance impact but it should be enough for high traffic app.

  2. Inflexibility of client

    Client now need to access BLL/Services by using services instead of normal objects.

  3. More Service for Different Type of Client

    Now you need to provide more service than needed. Such as WebRequestRetriever, MobileRequestRetriever instead of accessing to a mere IRequestRetriever and let the composition root wire up the rest.

Apologize if this answer boarden the topic (just realized after finished).

Community
  • 1
  • 1
Fendy
  • 4,565
  • 1
  • 20
  • 25
  • Ok, I think the bit I was missing was actually in your second sentence: Your bottom layers (interface, bll, dal, entities) are just a bunch of libraries". I never considered that the BO layer itself might not be referenced directly... but as a dependency injected into the UI layer! I can certainly see the power in being able to swap out the business layer (e.g. for automated UI testing) but generally, I want ALL production interfaces to go through my real business layer (that way I know that all business rules have been run). – Colin Smith Jul 29 '13 at 12:24
  • Exactly. Your main concern is just to prevent UI to access DAL directly. It can be two ways, one is costly at development (web service) or costly at testing (using traditional composition root). In my point of view, unless you do critical system such as banking or flight system, or multitenant SAAS, using webservice is an overkill. Moreover, you will have it covered in integrated test too. – Fendy Jul 30 '13 at 02:31
0

IMHO:

It depends on who does the injection !-

It seems you need to/expect to have an MVC or MVP architecture to be in place, where a controller or Presenter does the job of translating the UI calls to business objects ,back and forth - Creating concrete implementations of IDataAccess, Sending it to Address class. So that the UI is totally unaware of who is providing the data it needs, and it provides you the expected scalability.

Thanks Tarriq

Tarriq
  • 13
  • 5