Oren posted about his "2 project" solution structure recently. It seems that developers are caught on this preconceived notion that a Visual Studio project must be directly related to a compiled assembly. True, by default this is how Visual Studio based compilation works. If your solution has eight projects in it, there will be 8 assemblies output from it when you do Ctrl-Alt-B. Each assembly will also, by default, be named exactly as the project is named. You can change the naming by opening the project properties and altering it to differ from the project name there, but there is no way to break the 1:1 relationship between projects and assemblies.
I think that it's because of this default behaviour that developers think in this manner. It doesn't have to be this way though. In Visual Studio, projects are organizational groupings of code artifacts. Traditionally we have used these project containers to organize our application structures based on logical, and physical, layering that we have designed. By that, I mean we will create a project to define hard visible boundaries between each of our logical layers. As a result we end up with a project for data access, another for the user interface, one for utility classes, a project for services, and so on. These demarcations of logical layers are there to visibly indicate to the programmer that there is a boundary.
Some would argue that separate projects allow you to better enforce dependencies between them. You can initially setup your project structure and add the appropriate references between them. After that there no longer is a need to alter references, and this fact alone becomes the guardian against rogue layer interaction. Honestly, I can see and appreciate this point. In the past, I haven't had much trust in my development teams and their ability to maintain proper layer separations. I've used project boundaries to comfort me in these times.
If you move all of your code into one project and use folders and namespaces to distinguish the layering in your application, there is nothing stopping you from quickly and easily corrupting the layering of your application. It's easy to add a 'using' or 'imports' statement and quickly get the UI to directly reference the code in the repositories layer instead of the services layer. There's no question about that. My question for you is this: What benefit does project based separation give you when I can simply hit Alt-Enter and have ReSharper add a reference to another assembly in a fraction of a second? Even with the hard layer separation that is provided by a project structure, it is still exceedingly simple to screw up that layering.
As Oren pointed out, the only sure fire way to stop this from happening is to start using something like NDepend. Developer diligence and self policing will go a long way as well, but if your project's morale starts to drop off, or if you have a lot of junior types, diligence alone isn't going to be enough. I think that the minimalist project approach does fantastic things for IDE performance, tool performance and code organization simplicity. I also thing that you need to evaluate your solution/project structure on a project by project basis. In some scenarios you may have no choice but to create many different projects. The key, I think, is in trying to keep that count at a bare minimum.
Like Ayende said in his post, I to am moving towards a two project solution as well. Actually, I'm moving to a three project solution, but only two of those have code files in them. For me it breaks down like this
The first two projects are fairly straight forward. The application project has any code that will need to be released to the production server and/or client PCs. The testing project holds, well, testing code which will not be released to production. The final project, which is for build artifacts, contains any files that assist in the compilation process. This project will include files that are never deployed to production. Some may be used as templates for creating files that are required in production. I'd like to do another post to fully explain the build project so let's not get to deep into it here.
I first saw the Build project in something that JP was doing. I seem to remember him also saying that this was something that he'd picked up from Jay Flowers. At the time that I saw this I was trying my best to include the items I needed to run my build process in the MyCompany.MyApp project. To do this I was using a solution folder named 'build'. As I added more and more items to the solution folder, I was want for more. I needed more structure, more control and better discoverability. The Build project is giving this to me.
In the Build project I employ roughly the following folder structure. I say roughly, because every application has different compilation, deployment and architectural needs. This is what I default to when starting, but rarely end up with when I end.
At the root of the project structure I have files relating to my nAnt (MSBuild if you like) script. This usually includes a *.build file (the nAnt script itself) and a b.bat file (the fast way to launch my nAnt script at the command line). Other things can happen into the root as well, but these two files are always there.
The Scripts folder is where I keep any scripting that is required by the build process to setup the environment. This would include vbs files that I have for creating virtual directories, application pools and other IIS related tasks. These scripts are all related to supporting environment configuration. Lots of the files that I put into this folder are found on the computers where I will run them. I include them here in the interest of maintaining the ability to download from source control everything that is needed to make the first compilation on a new computer work without needing to perform any installations.
The SQL folder is pretty self explanatory. Ì fraction it into on folder for DDL scripts and another folder for data scripts. The scripts in the data folder should only include information that is needed to prime the production database into the minimal working state required by the software. If you need to prime a database for testing purposes, those scripts should be kept somewhere else.
The templates folder is where I keep any files that are expanded and included in a release package. This usually means that I have all my configuration files in this folder. I regularly tokenize these files and use nAnt to insert values depending on the environment in use. My basic thought process is that any Xml based configuration file that exists in my application resides here. I'll leave the discussion of the ramifications of this to another blog post.
So overall, I like a minimalist approach to solution and project structures. Two projects just isn't enough for me though. I'd rather have that third one for my build/compilation/release artifacts.
Kyle posted about an ongoing conversation that he and I have been having for the past couple of weeks about code compilation practices. As expected, there was some disagreement with the practice that I use and, as a result, I wanted to clear up some of the points in the comments and rebuttal posts. I'm going to make a couple of posts here. One that discusses compilation practices and another on project structures. Let's get on with the compilation practices here.
First let's talk about why I went this route. Last year I was working at a client that had 56+ projects in the solution....and that number was growing. Part of what I needed to do on that project was to make the development effort more manageable. At the same time as I was starting to fight through the fog, I read a post by Scott Bellware (I'd link to it, but let's not go there right now) and I attended JP's Nothin' But .Net course in pretty close succession. At first, I scoffed at Scott's suggestion that we compile assemblies differently than we organized our solution/project structures. How dare he suggest something so radical...so non-conformist...so simple.
My first physical (as much as it could be) introduction to the practice was at JP's course. JP showed us how you could, with the <csc> task in nAnt, compile your entire application into one assembly using one relatively simple set of code. True, we were only being exposed to a scenario of compilation for testing purposes. I started to screw around with the concept on some project code (at home and at work) and it became apparent that there was one barrier. Compilation to one assembly didn't deploy very well. Why not deploy one assembly to each physical tier then?
The only reasons I could come up with that argued against one assembly per physical tier were:
- Code reuse
- Fractional deploy-ability
- Assembly size
Code reuse is one of those things that we talk about all the time. We talk, code and deploy with the hopes and dreams that our super-special-never-causes-a-problem-when-used string manipulation method will be used by every project from this day forth. The truth is, that rarely will that method see the light of project again. It's existence falls into the dark abyss known as our current project. Even if we do think of it when we move off to the next project, we've most likely deployed that method into an assembly that brings with it a bunch of other stuff we don't want or need. Instead of dealing with the excess baggage, for good or bad, we tend to fall back onto cut-and-paste code reuse. To me, cross project code reuse is a pipe dream that rarely comes to fruition.
Like code reuse, fractional deploy-ability is something that everyone talks about, praises and designs around when we initially start architecting our projects. Maintaining the pattern I laid out for code reuse, fractional deploy-ability is lost when the project matures. Although we have the capability, thanks to our diligence designing the code architecture, we rarely deploy just one assembly. Instead we default to a deployment of the entire application no matter how small or isolated the changes were. I'm never sure it this happens because we are scared of the potential problems that can come from a partial deployment, or if our build and deployment processes are optimized to work with the full application and thus have less friction. Regardless of why, fractional deploy-ability just doesn't seem to happen.
Assembly size was the final concern I could think of, and it was a stretch from moment one. Of course, when you compile most of your code into one or two or three assemblies, you're going to see that those assemblies are physically larger. My one negative thought on this was that larger assemblies take more memory space when loaded. Well....most of the assemblies that I've build for projects tend to have a combined physical size of less than 20mb. Once that number ran through my head, I decided that it wasn't that much memory at that size. I also decided that I should treat this like performance. Until I know that memory usage is an issue, I shouldn't worry about it. Don't prematurely optimize goes both for performance and memory usage.
With those thoughts in mind, and if they fit the scenario that you have at hand, why not deploy one assembly per physical layer? Now, this won't always work. Like everything you need to look at your situation and apply the concept that makes the most sense for you. That said, don't dismiss it out of hand like I initially did. Remember to always challenge your assumptions.