I have a layered solution as follows:

  • UI (User Interface)
  • BLL (Business Logic Layer)
  • DAL (Data Access Layer)
  • SharedEntities (A VS Project with entity POCOs only)

I’d like for the BLL to have a service called GetProductList() which is implemented in my DAL layer. I thought about defining an interface in the BLL and DAL implementation as follows:

Option A:

// Interface defined in BLL 
public interface IDataServices
{
  List<Product> GetProductList();
}

// Interface implemented in DAL
public class DataServices : IDataServices
{
    Public List<Product> GetProductList()
    {
      return //do some database work here and return List<Product>;
    }
}

If I want this to be implemented in the DAL then I would have to have the DAL project reference the BLL project in order to see the interface definition of IDataServices. Or, I could replicate the interface definition in the DAL but then I will end up with duplicate code to maintain (the same interface definition in the BLL and DAL).

Option B: Another way I could do this is to forget the interface idea and just make the following concrete class and method call in the BLL which the UI can use:

// Concrete class defined in the BLL
public class DataServices
{
    Public List<Product> GetProductList()
    {
         DAL aDAL = new DAL();
         Return (aDAL.GetProductList());
    }
}

This is easy enough but then the BLL sees the DAL and has a reference to it but is this really a bad thing here? As long as the BLL does not use any database objects (i.e. datasources, connect strings, etc.) to satisfy a request and the DAL conforms to matching the names of the services I define in the BLL DataServices class isn’t that enough? All the talk I hear about swapping in another database engine can still be done by just making sure the next DAL provides the same services the BLL identifies in the DataServices class such as GetProductList(). In this setup the UI still does not know anything about the DAL and the DAL does not know anything about the BLL. If I entertain the idea of using dependency injection to avoid instantiating the DAL it in the BLL it would mean instantiating it in the UI to be passed into the BLL. I would not want to do this which would give the UI access to the DAL methods.

Option C: I took a short look at the Unity Container but that tool suggested registering all the interfaces and concrete class upfront at the entry point which would have been the UI which in turn ended up giving the UI visibility to both the BLL and DAL which seemed even worse. I saw a reference to use MEF with Unity to get around the problem of the entry point seeing all the layers but also saw that if you do that you can’t really unit test such a configuration. Seems like a lot of work and complexity compared to option B.

Option D: And yet another option I have thought about would be to create a facade layer (another VS Project) between the BLL and DAL. This does not seem to make much sense unless I ended up with a lot of DAL methods which were of no concern of the BLL so they would have to be hidden; allowing the DAL Facade to only show what the BLL needs. If I were to swap in another database I would still have to create methods which the facade needed based on the needs of the BLL as I mentioned in option B.

So based on all this I’m thinking of going with option B but I would like some community input here. What else can I do which meets the following:

  • 1) Does not let the UI see the DAL
  • 2) Does not let the DAL see the BLL
  • 3) The solution still allows for all layers to be unit tested
2

There are 2 best solutions below

12
usr On BEST ANSWER

IDataServices should be defined in the DAL. but then the BLL sees the DAL and has a reference to it that's the natural way to do it. A project can reference the layer below it. If you don't allow referencing downwards there can be no references at all.

Note, that a Reflection reference is still a reference because you can't change the layer below without changing the layer above. Dependencies are not a compiletime-only concept. Option B does not add any good. It removes a compiletime dependency but does not remove the runtime dependency.

The point of removing a dependency from A to B is that you can change B without changing A. That's the entire point. Runtime dependencies still count.

Regarding C: You can make the UI ask the BLL to register its dependencies. The BLL can then ask the DAL to register. That way the UI is shielded from the DAL.

D: I don't know what this would accomplish.

Your constraints can easily be satisfied by making the DAL define IDataServices.

5
Jakub Lortz On

Your option A (interface in BLL, implementation in DAL) + and IoC container is the best approach.

When designing a complex software solution, you have to divide it into smaller pieces.

Some of these pieces will be crucial to the solution. They will be the reason why you're developing the software, not just buying an existing solution. You have to focus on them. These parts will be complex and hard to implement. You cannot do anything about it. They have to be implemented as best as possible.

There will be simple pieces too. Functionalities that are easy to implement, maybe even possible to buy. Try to make this parts with as little effort as possible. They are necessary, but they are not what you were hired for.

Focus on the hard parts first. The business logic will be complex there. The business logic will not depend on storage solution you choose - so don't make it depend on a DAL project in your system. Define interfaces for repositories of you entities (aggregates if you follow DDD) in your business logic layer.

You should be able to test the business logic thorougly. You cannot do it if the BLL depends on the DAL.

The UI should be completely separated from the business logic. The UI is just a set of use cases the user can perform. As such, it should present a limited information to the user, and only accept a limited information from the user.

To separate the UI from the BLL, use separate ViewModel and Command classes to show and accept information from the user. Use an additional application layer to orchestrate the BLL in each use case.

Such approach is well-known as Hexagonal architecture, Onion architecture or Clean architecture. It's also referenced in Domain Driven Design books.

Of course you will need a place where you put all these dependencies together. This place is a composition root and it should be as close as possible to the application entry point. If you don't want to reference all the layers in UI project, move the composition root to another project.