Merge redundant assemblies

Lately I have become a big opponent of a popular anti-pattern: people insisting on splitting up their application tiers/layers into 5-10 separate Visual Studio projects and adding references between them. Double that number of projects if you want corresponding unit test project for each layer.

In fact, removing them has become one of the first steps I take when inheriting a legacy code base. If I were writing a book on refactoring Visual Studio solutions, I would call it Merge redundant assemblies. Here’s a diagram:

You don’t need to split your code across 50 gazillion projects. Next time you think of creating a new project in your solution, please remember the following:

  • Visual Studio projects are for outputing assemblies. Namespaces are for organising code.
  • Assemblies only need to be split if your deployment scenario demands it. Putting a client API library into a separate assembly makes sense because the same API assembly may be used between many apps, or in different App Domains. Deploying your domain model or data layer into a separate assembly does not make sense, unless other apps need them too.
  • Each additional project slows down your build. A giant project with hundreds of classes will compile faster than a smaller number of classes split amongst multiple projects.
  • Crossing assembly boundaries hurts runtime performance. Your app will start up slower, the ability to perform inlining and OS optimization is reduced, and additional security overhead is enforced between assemblies. Assemblies are supposed to be big and heavy; loading lots of little one goes against the CLR.
  • You don’t need one test project for each assembly. One giant tests project is normally fine. The only case I have seen where it made sense to have separate test projects was for a client API which duplicated many of the server internal class names and we wanted to avoid overlap/namespace pollution.
  • Common sense should be used to enforce one-way dependencies. Not assembly references.

Patrick Smacchia has a good list of valid/invalid use cases where separate assemblies are appropriate here.