We have a business logic layer (BLL) that is tightly coupled to our data access layer (DAL). We make calls like this:
using (FooData data = new FooData())
{
data.DoSomething();
}
It’s important to note that all of our data classes are internal
, and they’re in the same assembly as the logic classes, so that only the BLL can access the DAL.
In order to decouple these (to help facilitate unit testing), one idea is to create IDataClass interfaces, like IFooData. At first I thought it may be a problem because we’d have to make our data classes public to implement the interfaces. But we should be able to keep the data classes internal, even though we still need the methods to be public:
public interface IFooData
{
void DoSomething();
}
internal class FooData : IFooData // Notice the class is internal
{
public void DoSomething(); // Method is public, but that's ok because class is internal
}
So even though the method is public, since the class itself is internal, we should still allow only our BLL access.
Is there anything inherently wrong with this approach? Is there a better way to abstract the DAL, for unit testing, without opening the DAL to the world by making it public?
6
Extracting an interface and ensuring the BLL is using only interface types is a good way forward to make the BLL testable without the database.
What you are doing is absolutely fine.
2
Technically, there is nothing wrong with your design.
However, I would consider an alternative approach: instead of decoupling your BLL from your DAL, better decouple your DAL from your database. Allow creation of DAL objects in-memory, and if your BLL needs to load DAL objects from somewhere, use the repository pattern (see here) to avoid direct contact of the BLL to the database. That way, you can
- create unit tests for you DAL objects easily without the need of a database
- create unit tests for your BLL by providing in-memory created DAL objects through a mock repository as test data (ok, you can argue now if those tests should be really called unit-tests)
In fact, what you loose is the capability of testing the BLL with DAL mocks. But in reality, those DAL mock objects will typically look a lot like your real DAL objects to provide a base for useful tests of the BLL. IMHO it avoids a lot of code duplication and unnecessary effort when re-using your real DAL objects for automated tests of the BLL.
You can use the InternalsVisibleToAttribute
in order to break your DAL out from your Business Layer, but still keep the DAL methods internal. You’ll end up creating your DAL as a friend assembly.
This may not be a great approach as you would have to specify in your DAL’s Assembly.cs
file which assemblies can access the DAL. So there’s still some coupling involved (DAL knowing about BLL).
So, to answer your question, there isn’t anything wrong with your approach. But taking advantage of friend assemblies can provide you a bit more abstraction and probably help you out with making your code more testable.
I hope this helps.
4
You seem immoveable on your ideas that you expounded in your question, but I’ll tender an answer anyway – someone may find it useful sometime in the future.
If you insist on keeping your DAL internal but still want to hit it with some unit tests and don’t mind using the Microsoft Unit Testing tools, then try the PrivateObject approach. This is a wrapper for reflection that you could always code yourself, but it saves you a few lines of typing.
Note that using InternalsVisibleTo is a viable approach, but it’s clumsy – if you are using it then you may already be on a fast slide downhill. internal
means something is public only within the bounds of it’s assembly, that modifier is normally used because you don’t want stuff useable from outside that assembly, then you promptly turn around and violate that concept by declaring a InternalsVisibleTo… that is a big dirty red flag that is screaming out for some refactoring. Decoupling is a waste of time when only one assembly is calling the other, you may as well glue them together. Decoupling infers that multiple assemblies are calling the target (introducing unit testing doesn’t count towards the ‘multiple’ because there are ways to get to the target assembly like I’ve already mentioned).
3
The interface approach is a good / mainstream one for mocking out your DAL during testing of consumers like the BLL.
One thing to note however is that by making your DAL concrete classes internal, is that you may be making your concrete DAL harder to test directly, as this now implies that your DAL unit tests must also be in this assembly without needing to use back doors like reflection.
Also, at some point in future you may consider using an IoC container to build up the implementation behind an interface, which also might trip up on the internal.
You could mimic the encapsulation you are trying to achieve with ‘internal only’ by adding a convention / LINT rule that your BLL / other layers must couple to the DAL via the interface, not on the concrete implementation.
1