views:

108

answers:

3

We currently have quite a few classes in a project, and each of those classes implement an interface, mostly for DI reasons.

Now, my personal feeling is that these interfaces should be put into a separate namespace within the same assembly (so we have a MyCompany.CoolApp.DataAccess assembly, and within that there's an Interfaces namespace giving MyCompany.CoolApp.DataAccess.Interfaces).

However, somebody has suggested that these interfaces should actually be in their own assembly. And my question is - are they right? I can see that there are some benefits (eg. other projects will only need to consume the interface assembly), but at the end of they day all of these assemblies are going to need to be loaded. It also seems to me that there could be a slightly more complex deployment issue, as Visual Studio will not automatically pull the implementing assembly into the target's bin folder.

Are there best practice guidelines for this?

EDIT:

To make my point a little clearer: We already separate UI, DataAccess, DataModel and other things into different assemblies. We can also currently swap out our implementation with a different implementation without any pain, as we map the implementing class to the interface using Unity (IOC framework). I should point out that we never write two implementations of the same interface, except for reasons of polymorphism and creating mocks for unit testing. So we don't currently "swap out" an implementation except in unit tests.

The only downside I see of having the interface in the same assembly as the implementation is that the whole assembly (including the unused implementation) will have been loaded.

I can, however, see the point that having them in a different assembly means that developers won't accidentally "new" the implementing class rather than have it created using the IOC wrapper.

One point I haven't understood from the answers is the deployment issue. If I am just depending on the interface assemblies, I'll have a something like the following structure:

MyCompany.MyApplication.WebUI
    References:
        MyCompany.MyApplication.Controllers.Interfaces
        MyCompany.MyApplication.Bindings.Interfaces
        etc...

When I build this, the assemblies that are automatically put into the bin folder are just those interface assemblies. However, my type mappings in unity map different interfaces to their actual implementations. How do the assemblies that contain my implementations end up in the bin folder?

+2  A: 

The usual practice is to place them in their own assembly, because then a given project consuming those interfaces doesn't require a hard reference to the implementation of those interfaces. In theory it means you can swap out the implementation with little or no pain.

The difficulty in this comes in when you would like to create an instance of that interface - but DI frameworks solve this problem.

In a deployment scenario, if you changed the implementation you could in theory just deploy that single DLL - thus leaving, say, the UI and interface DLLs alone. If you compiled your interfaces and implementation together, you might then need to redeploy the UI DLL...

Another benefit is a clean segregation of your code - having an interfaces (or shared library) DLL explicitly states to any on the development team where to place new types etc.

I don't know if there are best practices for or against, the important thing arguably is that in code, you are always consuming the interfaces and never letting any code leak into using the implementation.

Adam
@Adam Oops sorry about that, edited wrong post.
chibacity
+2  A: 

The pattern I follow for what I call shared types (and I too use DI) is to have a separate assembly which contains the following for application level concepts (rather than common concepts which go into common assemblies):

  1. Shared interfaces.
  2. DTOs.
  3. Exceptions.

In this way dependencies between clients and core application libraries can be managed, as clients can not take a dependency on a concrete implementation either directly or as an unintended consequence of adding a direct assembly reference and then accessing any old public type.

I then have a runtime type design where I set up my DI container at application start, or the start of a suite of unit tests. In this way there is a clear separation between implementations and how I can vary them via DI. My client modules never have a direct reference to the actual core libraries, only the "SharedTypes" library.

The key for my design is having a common runtime concept for clients (be it a WPF application or NUnit) that sets up the required dependencies i.e. concrete implementations or some sort of mocks\stubs.

If the above shared types are not factored out, but instead clients add a reference to the assembly with the concrete implementation, then it is very easy for clients to use the concrete implementations rather than the interfaces, in both obvious and non-obvious ways. It's very easy to gradually end up with over-coupling over time which is near impossible to sort out without a great deal of effort and more importantly time.

Update

To clarify with an example of how the dependencies end up in the target application.

In my situation I have a WPF client application. I use Prism and Unity (for DI) where importantly, Prism is used for application composition.

With Prism your application assembly is just a Shell, actual implementations of functionality reside in "Module" assemblies (you can have a separate assembly for each conceptual Module, but this is not a requirement, I have one Modules assembly ATM). It is the responsibility of the shell to load the Modules - the composition of these Modules is the application. The Modules use the SharedTypes assembly, but the shell references the concrete assemblies. The runtime type design I discussed is responsible for initializing dependencies, and this is done in the Shell.

In this way module assemblies which have all the functionality do not depend on concrete implementations. They are loaded by the shell which sorts the dependencies out. The shell references the concrete assemblies, and this is how they get in the bin directory.

Dependency Sketch:

Shell.dll <-- Application
  --ModuleA.dll
  --ModuleB.dll
  --SharedTypes.dll
  --Core.dll
  --Common.dll + Unity.dll <-- RuntimeDI

ModuleA.dll
  --SharedTypes.dll
  --Common.dll + Unity.dll <-- RuntimeDI

ModuleB.dll
  --SharedTypes.dll
  --Common.dll + Unity.dll <-- RuntimeDI

SharedTypes.dll
  --...
chibacity
This model implies a single assembly with many interfaces defined in it, which may represent functionally different things (from my example, you'll have interfaces which relate to Controllers, some interfaces which relate to DataAccess, etc.) Within that "shared" assembly, some interfaces may rely on other interfaces. If you're creating a new application and want to reuse your data access (for example), you'll need to include a reference to that shared assembly. How do you then know what other assemblies and types you need map through unity?
David_001
@David_001 The example is not meant to cover every conceivable permutation of this pattern, that would be impossible. It can be adapted to circumstances. The key thing is it separates modules and their dependencies on concrete implementations.
chibacity
A: 

The answers so far seem to say that putting the interfaces in their own assembly is the "usual" practice. I don't agree with putting unrelated interfaces into one "shared" common assembly, so this would imply I will need to have 1 interface assembly for each "implementation" assembly.

However, thinking about it further, I can't think of many realy world examples of this practice (eg. do log4net or nunit provide public interface assemblies so that consumers can then decide on different implementations? If so, what other implementation of nunit can I use?). Spending ages looking through google, I've found a number of resources.

David_001
I hate to accept my own answer, but as the other answers (currently) don't address my specific scenario, or give references for further info...
David_001
@David_001 I don't think you're being entirely representative of the other answers when proving your point, of which you have accepted your own answer. BTW log4net and NUnit are not applications, they are libraries. I don't think that's too subtle a point.
chibacity