Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where should class Interfaces be placed in a .NET class library? [closed]

I'm curious to developers' opinions/practices on where should class interfaces, ideally, be placed in a .NET class library. Should they be contained within their own dedicated library within the solution e.g MyCompany.AccountPackage.Interfaces?

Should they have their own namespace e.g. MyCompany.AccountPackage.MasterAccounts.Interfaces?

Any other thoughts/opinions appreciated?

Is there any good guide demonstrating how to structure a .Net library(or even better, the solution), showing in general, what classes/interfaces should typically be exposed in the standard library.

Thanks, d.

like image 541
user989046 Avatar asked Jan 10 '12 08:01

user989046


3 Answers

Short answer:

Interfaces should be close to where they are implemented.

At least some of the BCL does this - IEnumerable<T> lives in the System.Collections.Generic namespace, for example.


Slightly longer answer:

It depends on what the purpose of the interfaces is.

Is it to provide a plug-in interface (a-la the provider model or MEF) for third parties?

If so, a separate assembly can make sense, so all that needs to be imported by the third party is this assembly.

Is it internal, for testing and IoC purposes?

I would argue that keeping it close to the implementation makes sense, organizationally and to help with keeping the code in sync with the interfaces.

like image 72
Oded Avatar answered Oct 04 '22 15:10

Oded


I think the answer is rather contextual in that it depends what the interfaces are for and how they fit into the architecture of your library.

Object Model

In my opinion, if you are creating some form of object model, you should put the interfaces that represent the object model in one project within an assembly. This way, the references to the interfaces can be re-distributed and coded against without having to worry about the actual implementation, which could vary. It also allows for you to provide an initial implementation of the object model, while leaving it open to re-implementation by someone else (perhaps to support a new platform or related software).

Generic Use

If you are intended on creating an interface similar to those found in .NET's System.Collections.Generic namespace, which are going to be re-used in many places; then I would suggest placing it low down enough so that multiple project can access and implement the interface. However, this does not mean you cannot implement the interface yourself within the same project. In that case, it all depends what your intending to be within that assembly.

Other Use

Another use may be some database interaction system, where you have an interface that defines some database interactor, but you may have two or more implementations of it. For example, you may have one that interacts specifically with Microsoft Access databases, and one that interacts with Sql Databases. In this case, it would be feasible to provide your interface(s) and implementation(s) in the same assembly.

All that being said, it does all depend on the situation and intended use. Best way that I have found to determine what that is, is to create a prototype version of the assembly and see how you may need to use it.

like image 33
Samuel Slade Avatar answered Oct 04 '22 16:10

Samuel Slade


As in most situations: it depends.

But in 99% of all cases I put interfaces into their own assemblies, grouped into logical units to prevent tight interface dependencies. In former days I just put it where the first implementation was. This grew into a tightly coupled mess that was impossible to split up later.

If you practice/strive for loose coupling, I suggest moving interfaces to seperate assemblies. I prefer to only include:

  • interfaces,
  • enumerations
  • constants
  • extension methods for the interfaces (not depending upon specific implementations)
  • "stable" classes and structs (i.e. those that will never ever change or be inherited)

into "interface assemblies" and to only reference:

  • other "interface assemblies"
  • "tools assemblies" (which should themself only contain stable stuff like extensions methods, constants)

This doesn't apply to internally used interfaces only used in one assembly (which will be the 1% of interfaces).

The only implementation that can be included in the same assembly as the interface itself is one, that:

  • doesn't rely on other non-interface assemblies (you don't want to include stuff to use an interface assembly that's only needed for one implementation you don't want to use).
  • is some kind of default or no-op implementation

The "implementation assemblies" should only be referenced in the main assembly, other assemblies only include the interface assembly. This also ensures that you don't use the implementation directly or test if the interface is of this special implementation.

Why? This way your assemblies only rely on stable assemblies and are not tightly coupled. This is also the preferred way if you plan to use dependency injection.

Namespaces:

You should put the implementations in a sub-namespace of the interface. So, to pick up your example, I'd suggest:

  • putting the interface in MyCompany.AccountPackage.MasterAccounts
  • putting extension methods, other stable stuff for this interface also in MyCompany.AccountPackage.MasterAccounts
  • putting an implementation in a properly named sub-namespace MyCompany.AccountPackage.MasterAccounts.SQLMasterAccounts or just MyCompany.AccountPackage.MasterAccounts.Implementation.

Why?

From the implementations side this means, you get all interfaces and extension stuff without having to use using MyCompany.AccountPackage.MasterAccounts.Interfaces.

From the clients side this means that you will be shielded from too much options from intellisense in the implementations assemblies (which shouldn't include the other implementation assemblies anyway), yet have easily access to the implementations in the main assembly, where you choose the concrete implementation to use.

like image 39
Onur Avatar answered Oct 04 '22 15:10

Onur