1

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
Robertcode
  • 911
  • 1
  • 13
  • 26

2 Answers2

2

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.

Jakub Lortz
  • 14,616
  • 3
  • 25
  • 39
  • I tried a composition root approach at the main() entry point for a WinForms app but that gave the UI access to everything. I did not try creating a separate project to place it in so I think I'll give that a try and have the UI call that composition root project at startup. One challenge though is that I have a unit of work pattern class governing a generic repository which is set to support 2 entities. The unit of work class implements IDisposable so if it get disposed then its instance is invalid. That was one thing I have to investigate for a container to deal with. A lot of work. – Robertcode Nov 28 '15 at 01:29
  • @Robertcode Move the UI (all forms etc.) to another project. Make the `Main` method your composition root. Then your root is a separate project - it is the only place that references all other projects. The other projects strictly follow the hexagonal/onion/clear architecture principle of not depending inwards. – Jakub Lortz Nov 28 '15 at 01:43
  • The BLL can depend on the DAL in an abstract way. It would not depend on a concrete DAL. For that reason it can be tested, too, my injecting a mock DAL. Can you give some reasons why the BLL cannot even reference the DAL in an abstract way (e.g. through interfaces)? I think that's a reasonable choice as well but I don't know why one would do it that way. – usr Nov 28 '15 at 11:46
  • I have created another question related to this one but focused on getting an answer to implementing this using the Unity Container. The link is here: http://stackoverflow.com/questions/33976371/how-do-i-use-unity-container-to-instantiate-a-dal-class-from-a-bll-class-in-a-wi The original question here will remain as it provides some information about implementing without Unity. The Unity path does add complexity. A non Unity approach may not be as elegant but may be a more pragmatic approach. I have not decided yet. – Robertcode Nov 28 '15 at 21:06
  • The solution for me is to use a Pure Dependency Injection approach rather than a container or BLL seeing DAL method. This solution was discussed in the following question I asked on StackOverflow which is related to this page: http://stackoverflow.com/questions/33976371/how-do-i-use-unity-container-to-instantiate-a-dal-class-from-a-bll-class-in-a-wi – Robertcode Nov 29 '15 at 05:28
1

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.

usr
  • 168,620
  • 35
  • 240
  • 369
  • BLL (or Domain Model) is the lowest layer. It should not depend on (reference) any other layers. The business logic implemented in BLL is complicated enough to avoid any technical complexities coming up in infrastructure layer (DAL etc.) – Jakub Lortz Nov 28 '15 at 00:00
  • @JakubLortz that's a strange model. Normally, the BLL is between UI and DAL. Why would the DAL (in your model) reference the BLL? Why would the UI reference the DAL? That is what you are saying. – usr Nov 28 '15 at 00:00
  • I edited it to say the shared entities is only a project of POCOs so any layer can reference it for data layout. It has no operations. – Robertcode Nov 28 '15 at 00:05
  • @Robertcode OK, so? To me it seems that SharedEntities is immaterial to the question. Does my answer help you? – usr Nov 28 '15 at 00:06
  • When you consider the flow of the data, it is. But the business logic layer is the most important layer in a complex software system, so it's reasonable to make it independent on the infrastructure (including data access). It can be done by using dependency inversion - defining the required interfaces in BLL, that are later implemented in DAL. Check the information about haxagonal architecture or onion architecture, or any DDD reference. – Jakub Lortz Nov 28 '15 at 00:07
  • @JakubLortz let's hear from the OP what he wants to do. It is possible to do it that way, yes. In his list of layers it is not that way. – usr Nov 28 '15 at 00:13
  • I am only thinking of B in the question which is not to have DAL contact BLL or UI contact DAL. BLL is between UI and DAL. I've seen some say BLL should not reference anything articles but have yet to see an example implementation of that in C# for a Winforms app meeting my bottom 3 items. Putting the IDataServices interface into the DAL is close to what I was trying to do. I can have the BLL make an instance of that to use. – Robertcode Nov 28 '15 at 00:15
  • @Robertcode this is what I understand. Does this answer the question? – usr Nov 28 '15 at 00:20
  • Your comment so far appears acceptable based on what I know. But I think it is too soon to close this. I would like to see if anyone has anything new to say. This whole topic is very confusing all over the internet. Someone like Microsoft should simply provide working model projects implementing n-tier with layers (UI, BLL, DAL) along with working alternatives if there are any covering WinForms, WPF, not just asp.net or mvc. – Robertcode Nov 28 '15 at 00:27
  • @Robertcode modularizing code depends 99% on the app. There is no generic model that fits all. Beginners often blindly implement something like a 3-tier architecture hoping that it helps overcome structural challenges. If you follow the classic 3-tier model you are not guaranteed to have an easy time. Therefore, samples have limited value. – usr Nov 28 '15 at 00:37
  • Okay usr. I'm going to close out and accept your suggestion which is close to what I was trying to do. I believe I saw an architecture article someplace that offered a path of BLL referencing DAL so I'll just go with that for now. I found the following page that I will investigate for more information: http://layersample.codeplex.com/ – Robertcode Nov 28 '15 at 00:53
  • @usr Check my answer to find more arguments for this approach. IMO hexagonal is a standard way to implement layered architecture, just as Tasks are a standard way to implement async operations in .NET – Jakub Lortz Nov 28 '15 at 01:03