Patrick Peters
Patrick Peters

Reputation: 9568

Strategy tips on partitioning assemblies and namespaces

A very common complexity for technical architects is to divide the application in assemblies and namespaces.

Also: namespaces can span multiple assemblies.

I had a bad experience in a project once where we partitioned assemblies according logical units of the application. This decision ended up with solution files with 30 or 40 projects! The master solution file loadtime was approx. 5 minutes!!! This ended up in a great waste of time, pff...

The opposite scenario was to hold all code in 1 assembly and partition when it is really needed.

Do you have additional tips or best-practices regarding this issue?

Upvotes: 5

Views: 1660

Answers (5)

George Mamaladze
George Mamaladze

Reputation: 7931

Motivation

The reason why I am posting this late answer is that all previous answers are more recommendations rather then consistent best practices.

Since many years I am working in a very large .NET project and only consistent best practice which is both strategically and technically well motivated is the one proposed by NDepend team.

In a nutshell

NDepend recommendations are generally inline with your experience of not structuring assemblies according to architecture. It also contains considerations when to have separate assemblies and why. The rule of thumb is use structuring by assemblies for physical reasons, use namespaces for logical reasons.

NDepend best practice summary:

For assemblies and projects

  • Reduce drastically the number of assemblies of your code base.
  • Create a new assembly only when this is justified by a specific requirement for physical separation.
  • In a Visual Studio project, use ‘reference by assembly’ instead of ‘reference by Visual Studio project’.
  • Never use the Visual Studio referencing option ‘Copy Local = True’.
  • Put all VS solutions and Build action .bat files in a $rootDir$ directory.
  • Compile all assemblies in directories: $rootDir$\bin\Debug and $rootDir$\bin\Release
  • Use the directory $rootDir$\bin to host tests assemblies.

For namespaces

  • Use the concept of namespace to define boundaries of components.
  • A namespace typically contains from one to two dozens of types, and has a reasonable size that fits in the 500 to 2.000 LoC range.
  • Take the time to levelize your code-base components, it is certainly a cheaper task than expected, so the Return On Investment will be high.
  • Continuously check that the components’ dependency graph inside an assembly is acyclic.
  • If a component is too large (> 2.000 LoC), then use sub-namespaces to divide it into a smaller set of related components.
  • At any scale, classify components between high-level mediators, middle-level independent features, lowlevel base/domains.
  • Having a 'levelized' set of components removes the need for most design decisions.

Detailed reading

Partitioning code base through .NET assemblies and Visual Studio projects

Defining .NET Components with Namespaces

Upvotes: 0

Daniel Earwicker
Daniel Earwicker

Reputation: 116674

I find it helpful to organise namespaces into a hierarchy, and make the assembly names match the subnamespace they contribute to. For example, a project called Womble would have a top-level namespace Womble, and then there might be assemblies called:

Womble.ClientLibrary.dll
Womble.Controls.dll
Womble.Util.dll
Womble.Interop.dll

Here, the outer Womble namespace spans multiple assemblies, but each assembly has a unique subnamespace that only it can contribute to, which you find by removing .dll from the end. It makes it a lot easier to remember what you need to reference and to find things.

As for very large numbers of assemblies, ultimately you don't need to keep them all in one solution. In large scale development it helps to break up a big product into subsystems, which may themselves consist of multiple assemblies, and each subsystem may end up being maintained by separate teams, and can have their own solution files. The various teams "release" new versions to each other via source control, treating each other as third party libraries.

I don't think there is a hard and fast way to decide how to break up software into aseemblies. There's a general principle here: things that change for different reasons should be separated.

Very large projects can gain from the fact that by putting things in separate assemblies, you are able to patch them separately. You can produce a hotfix for an issue in Womble.Interop.dll, and then separately produce a hotfix for an issue in Womble.Controls.dll, and give both to the same customer, so that in theory, those two assemblies could be completely maintained and supported by separate teams, without having to coordinate their activities directly.

Separate assemblies also create clarity in the dependencies between code. You can see at a very high level (just looking at the references list) how one chunk of code depends on another, and how it might be reused. If you put everything in one assembly, it might be one big tangled mess with no sensible pattern to it.

Upvotes: 0

Eric Cosky
Eric Cosky

Reputation: 574

What has worked well for me is large level grouping by type of code, but more macro-level than the logical units you were splitting up by. For instance,

  • Engine - anything non visual. Support classes, base infrastructure code. Everything refers to this.
  • EngineUI - Relies on Engine, and obviously used for UI, but nothing specific to any one app.
  • EngineServer - Relies on engine, used by the (usually web) server build.
  • AppCore - App specific core functionality, no UI.
  • AppUI - App specific UI
  • AppClient - Uses AppUI, AppCore, EngineUI, Engine. The actual client app.
  • AppServer - Uses AppServer, EngineServer, Engine. The server app.

Each project has a hierarchy of namespaces and once in a while I find its useful to fork out a large body of code into another assembly but usually these keep things reasonably organized and manageable, even when there are many hundreds of files involved. Too many projects is definitely something I try to avoid. One bonus of keeping these projects to a minimum is that it makes it a lot easier to actually reuse these libraries in subsequent projects (such as automated testing builds).

I don't worry too much about unused code being deployed from these libraries because I can use a utility that strips out unused functions for the final build, keeping file size down to a minimum.

Upvotes: 0

Kostas Konstantinidis
Kostas Konstantinidis

Reputation: 13707

You can partition classes with Namespaces and use folders if you want to group source files together for easier maintenance. If you have security requirements and certain assemblies need to go through special processing such as Obfuscation for example, then you may need to separate those to a separate project.

Re-usability is also a factor that you may need to consider when thinking about whether a logical unit needs to get its own project since you may need this project in another solution as well.

Upvotes: 0

Grzenio
Grzenio

Reputation: 36649

I split code into separate assemblies only when I need to reuse it for two different applications (pretty much). So I start with everything in one project, and when the need to reuse code becomes obvious I create a new assembly and move the code (sometimes its obvious from the very beginning, e.g. when you need to have a web app and win forms doing same thing).

Re. Namespaces, I prefer to have it quite well partitioned within an assembly, so it is clear where each class belong and what it should be used for.

Upvotes: 1

Related Questions