Ayende @ Rahien

It's a girl

The two project solution

Back to technical content :-)

This post from Kyle Baley got me thinking about my last few projects. In them, I trended toward the assembly per logical layer of the app. So I had things like:

  • MyApp.Model
  • MyApp.Web
  • MyApp.Services
  • MyApp.Infrastructure
  • MyApp.Tests

It worked, but at one point I found myself managing 18 projects solutions, and that was only because I was committed to reducing the number (otherwise it would have been much higher). I hear about people talking about 30 - 60 projects in a single solution, and there are people with more. Considering the cost of just managing that (not to mention the cost for compilation per project), that sounds like a very bad approach.

Right now, I think that on the next project I'll have the following structure:

  • MyApp
    • /Model
    • /Web
    • /Services
    • /Infrastructure
  • MyApp.Tests

Everything that the application does will be in a single project, without the need to split it off to a lot of separate projects. I found that the vaunted isolation that we seem to seek is achievable easily without forcing artificial barriers between the different parts of the application. It also means shorter compilation times and easier deployment mode.

More than everything else, it means that I have less noise to deal with.

I would extend that to an assembly per physical location, so assuming that I had a smart client application, I would use the following scheme:

  • MyApp.Server
  • MyApp.Interfaces
  • MyApp.Client
  • MyApp.Tests

And that would be it.

Thoughts?

Comments

Arne Claassen
02/21/2008 10:52 PM by
Arne Claassen

I generally end up with 3 projects

MyApp.Library

MyApp.App (web, forms, wpf, service, console)

MyApp.Tests

This is mostly a habit because a lot of my projects end up sharing code between multiple UIs.

Steve
02/21/2008 10:56 PM by
Steve

I've been looking at using solution folders as a way to divide up some of the logic

Thomas Gravgaard
02/21/2008 11:15 PM by
Thomas Gravgaard

I have had the same experience. In my old job I spent a lot of time trying to "do things properly". Splitting up into a lot of logical assemblies, promoting reuse by having special helper assemblies and so on. The result was a huge amount of lugggage that each application had to carry around. When we deployed a simple windows service or console program, it consisted of 9 or 10 assemblies. And don't get me started on versioning all of these, because we were very methodical there too. It sort of worked, but only because we had build a very rigid CI monster controlled by a horde of NAnt scripts that no one - even me who wrote most of them - could figure out after two months away from them.

Today I consider that a learning experience, and I, like Arne, specifically make the three project solution. One for the Core of the application - split up using folder/namespace structure. A test library for the core lib, and then the application on top. If there are more apps they get their own project. All application projects have as shallow code as is can. All logic is put into the Core assembly - so if need be - it can be reused.

I find that it reduces friction both when developing, but especially when deploying. Only two assemblies to deploy. No inter version dependency nightmares and so on. Simple and smooth.

Scott Bellware
02/22/2008 12:23 AM by
Scott Bellware

Hmmm... I wrote about this in January last year and it was dismissed as heresy. That's it... I'm changing my name to "Ayende"! :)

Steve Campbell
02/22/2008 01:32 AM by
Steve Campbell

Separate projects is only important in as much as you want to enforce component boundaries (or layering) and achieve re-use. On small, or modularized software, I would see nothing wrong with combining everything into 1 or 2 projects. Folders within the projects can provide additional structure if needed.

Mike
02/22/2008 01:39 AM by
Mike

I like your final project breakup. I assume by the "Interfaces" project you mean no implementation classes not winforms/wpf?

Haacked
02/22/2008 02:15 AM by
Haacked

Dude, I am so with you on this! The number of projects in Subtext kills me sometimes and was a painful lesson.

The way I see it is to call YAGNI on starting with multiple projects. Start with 2, the app, the tests. Then split them into multiple code libraries if the need arrives and only then.

Dave Newman
02/22/2008 02:33 AM by
Dave Newman

Yep, thats exactly what I'm doing too. NAnt builds are so much shorter, visual studio's faster, resharper's faster, it's all good.

I did have an argument with a coworker though, he thought it wouldn't be setting a good example to other developers, because breaking the separation is so much easier. Problem is though, with resharper now if you reference a class in an unreferenced assembly, you can just hit alt-enter and add the reference automatically! So your assembly separation is an illusion anyway.

Once again, it all comes down to education.

Derik Whittaker
02/22/2008 03:04 AM by
Derik Whittaker

The only real issue with having everything in one assembly is there is not real way to force separation. It is up to the developer to know that the UI should interact with the db directly.

I know that you personally may not do this, but how about other devs on the team? Is someone (you) going to review the code (as it should be) to ensure proper separation?

Christopher Bennage
02/22/2008 03:07 AM by
Christopher Bennage

Ok, I really like this whole train of "why make things needlessly complicated?" and I'm already thinking about simplifying my next project, but...

I do find that separate projects encourages me to keep the layers clean. Either laziness or ignorance, could mix concerns across layers.

Of course, my "clean" is probably a needless complication.

Do the simplest thing that works, and nothing simpler.

Robert Ream
02/22/2008 03:27 AM by
Robert Ream

When using ActiveRecord on a project I like to use this structure:

MyApp.Model

MyApp.App

MyApp.Test

The reason I do this is that I'm a fan of the NHibernate Query Generator. Seperating the Model from the main project like this allows me to have a simple post build proccess that generates the query code into the main project.

dru
02/22/2008 04:00 AM by
dru

I hear what you are saying. On my list of things to try is JP Boodhoo's (I think) one project format and use nant to build out the assemblies as necessary.

-d

Darrell Mozingo
02/22/2008 04:07 AM by
Darrell Mozingo

I'm a huge fan of keeping things simple, too, but I've found one of the benefits to splitting out your library/business layer is the ability to unload the main GUI/application project, so you only have to compile your library and unit test projects when working on the back end. This usually helps us keep compile times down when we're not touching the GUI.

Udi Dahan
02/22/2008 06:52 AM by
Udi Dahan

I guess that I'll have to take the opposing view then :-)

First of all, project doesn't necessarily equal assembly. That's just the defaul of Visual Studio. You can use ILMerge (http://research.microsoft.com/~mbarnett/ILMerge.aspx) to take multiple projects and turn them into a single assembly.

Second, dependencies matter. Knowing who depends on what, and, more importantly, who should NOT depend on what is critical in scaling a project in terms of number of developers and size of code base. Being able to just look at the list of referenced projects and see a misplaced dependency is the first thing I look for in code reviews.

"Why do you have a reference to System.Data.SqlClient in your Domain Model?"

I find that I can catch problems much quicker.

In terms of speed of Visual Studio (for build or just in general), you shouldn't need to have more than a couple of projects open at one time anyway. You can have more than one solution.

When you're working on the Domain Model, do you REALLY need to have in front of you the javascript of the webpages? You should have the Domain Model tests, but not the FIT tests.

I find that once developers go down the unit testing / TDD route and stop having to run the entire system to see if the code they just wrote works, then having more granular projects is just fine.

I've used this style on projects as small as 3 developers and up to dozens of developers, web, smart clients, batches, you name it.

If you'd like to hear more, check out my podcast on the topic - How to structure .net solutions and components:

http://udidahan.weblogs.us/2007/04/18/podcast-how-to-structure-net-solutions-and-components/

Colin Jack
02/22/2008 08:53 AM by
Colin Jack

Yeah I'm with Udi, to me this is not necessarily a good approach for large pieces of work.

We do have a lot of projects, not least because we break out even our model into subdomains (CRM/Finance etc) to clarify the coupling between them. We could ofcourse just use seperate namespaces but this does make managing the dependencies a lot harder.

Chuggle
02/22/2008 09:12 AM by
Chuggle

Im also with Udi on this one.....we have a massive number of projects in a solution...multiple domains each with a service layer and an interface layer

One "solution" can involve 4-5 domains and each domain has a set of unit and integration tests each in its own assembly

Each project is subject to an NDepend CQL run which will fail the build if e.g you reference NHibernate in the domain

PCs these days are fast enough so compilation time is good (and since when should our architecture be driven by the specification of the developers machine)

I was interested in Kyles suggestion about compiling into a single assembly on deploy though

Ayende Rahien
02/22/2008 09:38 AM by
Ayende Rahien

Devid,

You set it up so it is harder to do the right thing.

If you need to call Users.Get(15); devs will use that instead of:

using(new SqlConnection())

Ayende Rahien
02/22/2008 09:40 AM by
Ayende Rahien

Darrell,

This is usually an indication that you have an issue somewhere there. If you are using a web site project, stop doing that!

For anything else, find out why it takes so long to build, compilation is really fast these days. Are you having some precompilation steps that are going on there?

Ayende Rahien
02/22/2008 09:53 AM by
Ayende Rahien

Mike,

Yes, interfaces contains shared interfaces and maybe message definitions.

Maor David
02/22/2008 10:13 AM by
Maor David

I agree with Udi. I have a lot of projects - I I need to merge them, I use ILMerge. Every layer or entity (not always, but mostly) has seperate project. The reusability is better and the dependencies is easier.

Casey
02/22/2008 10:57 AM by
Casey

Largely with Udi ... or somewhere in between perhaps ... current framework/project has 3 solutions, one with no projects (build files and deployment stuff), one with 4 or 5 projects (msbuild tasks, Core, and a couple of supporting ones), and one with around 9 projects ...

The great majority of the code within those goes into the /Core projects, the additional projects are largely facades onto Core code to abstract things like SharePoint, services, etc.

It is easily manageable, leaves clear definitions, and the last step of our build will be an ILMerge to make deployment better.

Colin Jack
02/22/2008 12:02 PM by
Colin Jack

@Ayende

On the compilation time, could you not also argue that if you seperate out two parts of a system and then only have coupling from one ot the other via interfaces (in an interface project) then the build time for the bit that references the interface will have gone down (as discussed in Working Effectively With Legacy Code).

Overall the build time is up (another project) but when building either bit the build time is better.

Dimitar Dimitrov
02/22/2008 01:29 PM by
Dimitar Dimitrov

A project, that I am currently working on has 153 Projects in the solution file. And none of them contains tests. It is real pain working with it even on decent machine - opening, updating and compiling seem to last forever.

Therefore I am strong proponent od the idea to minimize the number of projects. Less is more

Jeff Fritz
02/22/2008 01:38 PM by
Jeff Fritz

I am one of those with a solution with 40 projects in it... but I have some justification as to the shear size of the solution. Our solution supports a massive web application that has several application servers, communication layers, and UI layers.

We've built our own remoting / MSMQ server application architecture complete with fail-over and load balancing capabilities.

For the level of re-use we have between these libraries, you need to build out by adding more assemblies.

Jason
02/22/2008 03:00 PM by
Jason

I used to think a physical assembly per layer was better. Now I like having 3 projects. Foo, Foo.GUI, Foo.Test. Where Foo is all my layers, GUI is Con/Web/Win. Tests is all tests for Foo/GUI. Granted my projects are small compared to what most of you probally work on.

If a physical seperation of layers is needed, nant can do this quite easily. if the project directory structure follows convention over configuration then it makes the configs in nant that much easier as well.

Dan Ports
02/22/2008 03:21 PM by
Dan Ports

I like Udi's approach conceptually. The main problem I have with it is that by having multiple solutions, you may lose some ReSharperability. Sure, you can have a solution file that contains all the projects as Udi does in nServiceBus, but now if you add/remove a project you have to modify at least two solution files, and you lose some DRY-ness.

Mike
02/22/2008 06:38 PM by
Mike

We have solutions with project count in the 30-40 range. I would love to split them into multiple solutions, but then I run into the project vs binary reference issue. Since the type of reference is stored in the project, how do you make a solution that has a subset of projects?

For example

Solution A has projects Y and Z where Y has a project reference to Z

Now I want a Solution B with projects X and Y in it.

This fails for VB projects... someone said it works fine for C#?

Anyone have this working? Going to the single project would solve this problem for me...

knocte
02/22/2008 07:49 PM by
knocte

Mike, maybe you're simply stuck by the way VS works with this stuff.

MonoDevelop in the other side, for example, is able to include solutions inside solutions, which is the thing that would solve your problem IMO.

Ayende Rahien
02/22/2008 11:41 PM by
Ayende Rahien

Colin,

Not really.

The build systems are smart enough not to recompile without need.

And I have not found noticable change in compilatin speed until you are starting to talk about orders of magnitude more code

Ayende Rahien
02/22/2008 11:49 PM by
Ayende Rahien

Jeff,

sounds like you have a very good reason to do have lot of projects, you have a lot of physical deployment locations.

I would still probably take the infrastructure, put it in its own solution and reference the compiled stuff, instead of adding those (hopefully rarely changing) to the project

Bil Simser
02/23/2008 04:52 AM by
Bil Simser

Oh man, I could do a whole blog series on structuring solutions as I've done all of them and experienced the pain and suffering. I'm with Udi on this, but respect (and follow) what you're talking about.

I like to see projects split up by a) context boundaries and b) responsibliities. I would not like to have a folder called domain and one called database or something with everything lumped into one project.

My approach (which I refine all the time) is to add projects as needed. I start with a MyApp.Specs and start writing specifications. I use the folders as namespace dividers so there might be a folder called Common for the base classes and interfaces and say one called Invoice for the invoicing subsystem. In creating specs I'll just inline the domain classes I need. When I'm done I use R# and F6 to move them to their own file, then relocate them to a MyApp.Core when I have a few of them. I might have a MyApp.Core.Interface if I need the interfaces, but that's probably later in the lifecycle as the solution grows.

In the end I'll have (on a large project) 10 or so projects. However following Kyle's original post (based on what I picked up off JPs technique) I have two assemblies I create in my build scripts. MyApp.dll and MyApp.Specs.dll. It makes for doing unit testing and code coverage a snap and I'll just exclude items by namespace or classname and exclude the entire MyApp.Specs.dll from coverage. Easy, sleazy.

In the end, what I put into Visual Studio lines up (mostly) with the physical structure but what's built for testing is another story. For deployment, it depends on what I'm doing. If it's a Smart Client app I'm doing with ClickOnce, I'll split things up differently (usually by functionality) so I can trickle out a new version of a module dll without having to completely redistribute my entire codebase.

Alan Buck
02/23/2008 05:00 AM by
Alan Buck

I tend towards the solution with an App and Test projects for a particular functional domain that I have to deliver. In most cases this include a website so my solution has two projects and a website. The App project breaks up into Action (responses to user input), Domain, Presentation and Services (delivering data to the UI). The Test project usually mirrors this division.

However, the component infrastructure I leverage is broken up into what I refer to as a core solution of components. This has seven projects in it. The App projects is mainly where I wire up all of the dependencies that most functional domain will need I have a CoreFacilities that supports the data access layer. There is a data access layer using NHibernate. I have a Model project that contains domain objects that are used in most of the functional domains. The Utility project are the helpers that I need and I have a Web.Controls project that extends Web controls.

So I guess I do more of a hybrid approach. For the task I work most of the time on I take the App/Test/Website but I need the supporting components that make up the core. The core is rarely touched anymore.

Colin Jack
02/23/2008 09:26 AM by
Colin Jack

@Ayende

Don't know a lot about it but I would have thought that if you keep everything in one project then any change will force a recompile of the whole thing.

Ayende Rahien
02/23/2008 01:41 PM by
Ayende Rahien

Um, yes.

The cost tends to be on the per project compilatin rather than per file

Colin Jack
02/23/2008 09:37 PM by
Colin Jack

Ahh good, thought I was missing something obvious.

I haven't tried it out but I would imagine for a medium-large codebase the cost of recompiling the entire codebase every time you make any change must become prohibitive. Maybe I'm wrong though.

Obviously having more projects adds build time too but if you break out multiple solutions and/or use interface assemblies then you should be able to deal with it.

Ayende Rahien
02/23/2008 10:35 PM by
Ayende Rahien

NHibernate is a pretty huge project, and the compilation times aren't that bad.

I am not sure what the line count for NHibernate.dll is, but it is safe to say that it is probably the biggest that you are going to see.

Bryan Reynolds
04/02/2008 05:01 PM by
Bryan Reynolds

Its nice to see this debate. It is always about the speed at which you can move around. Would be nice if the IDE did not get in the way.

Comments have been closed on this topic.