2

I've been reading up on DI and from what I can gather, the structure your VS solution should be as follows:

Web (UI) Project references:

  • Data Access (*scratches head)
  • Business Logic
  • DTO

Business Logic Project references:

  • DTO

Data Access Project references:

  • Web (*scratches head again)
  • Business Logic
  • DTO

Plus, the interfaces for concrete implementations should be kept in the Business Logic project which the DA will implement in a concrete class.

The "classic" 3 layer structure is:

Web references

  • Business Logic

Business Logic references:

  • DA

(With DTOs referencing all layers).

What I'm trying to understand with the DI structure is, I appreciate it seems to help with separating each module for testing and not invoking concrete classes within classes - but with the way the references are set up within the project, there feels like there's a tight coupling with the layers? The UI has a hard reference to both the DA and BL (and therefore has to instantiate both the BL class and a DA class that implements the interface that the BL accepts as part of the constructor injection).

It somehow feels wrong the UI now has references to both BL and DA. If I wanted to say implement IMessage with a SendMessage() method, (swapping from an SMTP server to say a SMS provider), I'd still have to invoke a DA class in the UI and pass it into the BL. Feels wierd?!

It looks like the UI decides what implementation of data it wants via invoking an instance of a BL logic class, which accepts an concrete implementation of a data access class in the web layer?

I'm just trying to completely clear my head of the classic n layer structure in VS and be open to how handing this in the UI is good (The UI should only be worried about the UI right?). I think I just need that light bulb to go off via a simple explanation. If you can help, it would be very much appreciated!

P.S - I'm working my way through Mark Seemann's Dependency Injection in .NET book at the moment, so my head is a bit fried!

  • I'm not sure how you come to the conclusion that dependency injection results in an increased number of dependencies. Could you explain why you think that the data layer now needs a reference to the user interface for instance? – Dirk Feb 11 '14 at 12:36
  • From the examples I saw, the UI would create instances of the BL which takes in a DA instance that it needs? – user2241336 Feb 26 '14 at 17:11
  • DI only means that instead of creating new instances of objects in the classes that use them you supply those objects from the outside, e.g. using arguments or properties. This doesn't increase the number of dependencies. – Dirk Feb 26 '14 at 18:04

2 Answers2

0

It all depends on where you put your factories and how much responsibility you give to each of them. For instance, if, in you have the following factories

' In the data access layer project
Public Class DataAccessFactory()
    Implements IDataAccessFactory

    Public Sub New()
        ' ...
    End Sub

    ' ...
End Class

' In the business layer project
Public Class BusinessFactory()
    Public Sub New(daFactory As IDataAccessFactory)
        ' ...
    End Sub

    ' ...
End Class

Now, when you create a business factory in the UI, you first have to create a data access factory, in the UI layer, so that you can inject it into the business factory. So, in that situation, the UI layer needs to reference both the business and the data access layer projects. However, you could also give more responsibility to the business factory, like this:

Public Class BusinessFactory()
    Public Sub New()
        _daFactory = New DataAccessFactory()
    End Sub

    Private _daFactory As IDataAccessFactory

    ' ...
End Class

Now, the UI layer doesn't get to decide which data access layer it will use, but it also no longer needs a reference to the data access layer project. In the second example, only the business layer project requires a reference to the data access layer project.

It's a little less flexible, since you'd need to implement a separate business factory for each data access factory that you want to support, but you can make it more flexible by implementing it like this:

Public Class BusinessFactory()
    Implements IBusinessFactory

    Public Sub New()
        _daFactory = NewDataAccessFactory()
    End Sub

    Private _daFactory As IDataAccessFactory

    Protected Overridable Function NewDataAccessFactory() As IDataAccessFactory
        Return New DataAccessFactory()
    End Function

    ' ...
End Class

Notice, the NewDataAccessFactory method is overridable. So now, when you need to use a different data access factory, you can override that same method without having to duplicate the rest of the factory logic. For instance:

Public Class XmlBusinessFactory()
    Inherits BusinessFactory

    Public Sub New()
        MyBase.New()
    End Sub

    Protected Overridable Function NewDataAccessFactory() As IDataAccessFactory
        Return New XmlDataAccessFactory()
    End Function
End Class

Of course, all of the logic (non-factory) classes are still fully injectable. It's only the factory classes which are slightly less flexible. But, on the plus side, not only does your UI layer no longer need to reference the data access layer, but it also makes it easier to instantiate your factory objects. All you need to do is create the factory you need, rather than all its dependency factories too.

If you ever needed to make the factory's dependencies injectable, such as for unit testing your factory, you could still do so by making an injectable derived class, like this:

Public Class InjectableBusinessFactory()
    Inherits BusinessFactory

    Public Sub New(daFactory As IDataAccessFactory)
        MyBase.New()
        _daFactory = daFactory
    End Sub

    Private _daFactory As IDataAccessFactory

    Protected Overridable Function NewDataAccessFactory() As IDataAccessFactory
        Return _daFactory
    End Function
End Class

So, while doing so requires an extra step, it does mean that following this design does not mean that you are painting yourself into a corner. In the end, you need to weigh the pros-and-cons and pick what works best in your situation. I recommend trying implementing your factories both ways so that you can get a feel for both methods.

Steven Doggart
  • 43,358
  • 8
  • 68
  • 105
  • So you're saying move the reference from the UI for DA to the BL?? Because what I normally do within my BL is only expose a factory class to the UI that contains properties that instantiate BL classes I need (which in turn instantiate the mirror DA class for that BL class) – user2241336 Jan 25 '14 at 23:36
  • Yes. I'm saying that you *can* do that. You certainly don't have to, but it's not a bad option. – Steven Doggart Jan 25 '14 at 23:37
  • The rule for DI is to never create objects directly (using concrete types), so this option may *seem* wrong. But remember, creating objects from the concrete types is the entire purpose of the factory classes. As such, the factories don't really need to, themselves, follow that DI principle. They certainly *can* have their dependencies injected into them, if it is advantageous to you for some reason, but they certainly don't need to. – Steven Doggart Jan 25 '14 at 23:47
  • If the BL does create its own DA objects, like you said you do, then why would the UI need to reference the DA library? – Steven Doggart Jan 25 '14 at 23:57
  • Ok. I'm just trying to work out the de facto way of how the projects "should" reference each other given DI. The first example I saw gave the UI both reference to the BL and the DA. Usually, I have a BL factory class that instantiates internal BL classes the UI accesses (only point into the BL from the UI) e.g. ManagerFactory.MessageManager.GetMessage(101); So the BL could have a factory that instantiates the BL classes, which have constructor injections of interfaces the DA layer needs to implement? My only concern is that from what I've read BL should not have a reference to the DA... – user2241336 Jan 26 '14 at 00:03
  • I see. What you describe is a good way to do it, and DI doesn't have to change any of that. With DI, the business layer logic classes should definitely not reference any specific data access types directly, *but* the business layer factories can. *That*, i think, is the key distinction that you are missing. – Steven Doggart Jan 26 '14 at 00:13
  • It's starting to make sense now seeing this DI is a pattern/principle.... I suppose the project references don't mean much. It's more about how you invoke your classes (as in, you shouldn't "new" classes within certain classes, e.g. BL invoking a concrete DA). If that's the case, the classic n-layer structure is fine. Invoking concrete DA classes (via a factory for example in the BL) and then constructor injecting them in the BL is a way of separating the layers, making them more testable. Am I right in thinking that? Is that it?! – user2241336 Jan 26 '14 at 00:16
  • Yes. You've got it! :) the factory part was the hardest part for me to wrap my head around too at first. Everyone gives good example for how to do the injections, but not so much how to do the factories. Factories are a big part of DI. Yes, you want to never "new" anything, but obviously you must still "new" your objects somewhere. You can only hand off that responsibility for so long. Eventually you must do it somewhere. So, the goal of DI isn't to *remove* all "news", but rather to *move* them all into factory classes so they aren't mixed with your logic. – Steven Doggart Jan 26 '14 at 00:29
  • Wow! I always use the Factory Pattern in my BL. Allows me to control the construction of the BL classes (and stop other devs "newwing" anywhere and everywhere by making BL classes internal and the factory public). Giving them one point into the BL from the UI. So all I need to do is adjust my approach by moving new DA's into BL factories and use constructor injection for the BL classes. I think I put too much thought into the project references! And I agree about the "newwing", it has to happen somewhere. And factories are definitely the right place for it. Thank you Steven for your help! – user2241336 Jan 26 '14 at 00:48
  • No problem. Glad to help. Welcome to the world of DI. You won't regret it :) – Steven Doggart Jan 26 '14 at 00:53
  • So can somebody else confirm that putting a ref from the BL to the DA is okay when doing DI? Just want to make sure I'm not going down a route of inconsistency with other DI advocates? – user2241336 Jan 26 '14 at 12:07
  • Scrap my last comment. I've created a simple example and the the UI has to reference the DA and not the BL reference the DA. I've realised the DA must create it's own concrete implementations (penny dropped), and then be passed into the BL. The only way to do this is via the UI calling a factory on the DA. Otherwise the BL is relying on the DA for concrete implementation (not DI)! So I have one factory in the BL creating it's concrete classes and one in the DA creating it's concrete classes, with the DA instance being fed into the BL factory via method injection! I think I'm there! – user2241336 Jan 26 '14 at 15:32
0

You can separate your interfaces from its implementation by putting them in a different assembly and having all your DI configurations in a separate assembly. If you put all the DI configurations in a different assembly then your UI layer will no longer need to reference all the other layers. You can then also easily switch the configuration assembly to quickly change the DI configuration without affecting UI layer.

Ankit Sinha
  • 421
  • 3
  • 11