When software projects grow both in age and size the developers often struggle with the structure of the code. It gets more and more difficult to find the class you have to change for a new requirement. In this post, I’ll show you how we organize our code and how we derive our structure directly from the requirements.
When you look at example code from the internet or how some frameworks (e.g. ASP.NET MVC) structure their code, you see that they most likely organize the code by layer or put classes of the same kind in the same place.
For example in Model-View-ViewModel and ASP.NET MVC samples you will see namespaces/folders named Views, ViewModels, Controllers, Entities, DataTransferObjects, Repositories and so on.
Layers they say!
Now, we all were told that layers are good. Mainly because they simplify the code due to the fact that layers higher in the hierarchy are only allowed to access layers further down in the hierarchy. That’s okay, but has nothing to do with structuring code.
I made the experience that structuring code by feature results in a much simpler layout. The main reason for this is that all the classes you are currently working on are physically near to each other despite their kind. The view, view-model and so on of the customer search are all in the same place: under the namespace MyCompany.MyProduct.Customers.Search. Thus minimizing navigation in the solution explorer (Visual Studio). Furthermore, these namespaces do not grow when the software grows, only the number of namespaces increases due to new features being implemented. Now when I have to look for a class then I start by looking up the feature it is needed in, go to this namespace and find it there.
The namespace per feature approach gives you some additional advantages:
- when a feature has to be removed you can “simply” remove the namespace as a whole and you do not have to search through all the layer based namespaces
- the structure of your code reflects the features and not the technologies used (watch Architecture Deference to see why this is important)
- refactoring namespaces (e.g. introducing sub-features) is simple because only the feature you are currently working on is affected
“But what about assemblies?”
Remember that assemblies are used to deploy a program on a machine. If you have a client and a server you’ll need at least two assemblies, probably at least three because of shared code. But this is a completely different decision than structuring. When you split up your code into different assemblies to deploy to different sites, you should still use the same namespaces in these assemblies. Start with changing the default namespace in the project settings.
Agile, Scrum, User Stories, Product Backlog and getting a structure by feature
When you develop software in an Agile way and your requirements are given in the form of User Stories then deriving features and therefore namespaces is really simple.
- Identify the Themes in your Product Backlog (see Themes/Epics/User Stories)
- Per Theme introduce a namespace. E.g. MyCompany.MyProduct.Theme1
- Identify features within each Theme. Often these features are initially described by Epics. E.g. searching, editing
- Per identified feature introduce a namespace below the Theme: MyCompany.MyProduct.Theme1.Feature1
- When namespaces grow too big, introduce sub-namespaces according to sub-features. Always try to think in user actions and not in technological differences.
“But what about infrastructure code?”
Yes, there is code that is used by several features. This code should be put into the existing namespace per feature hierarchy and not into its own. You got this right when you don’t need to add using statements into each code file (.Net, import for Java).
Code shared by MyCompany.MyProduct.Theme1.Searching and MyCompany.MyProduct.Theme1.Editing should be in MyCompany.MyProduct.Theme1.
“But my assemblies get really big! That slows me down because the time needed to build increases, especially when practicing Test Driven Development!”
I develop software test driven. That means that I build my code and run tests every few seconds (I try at least). Build time gets very important. Additionally, Visual Studio and Resharper don’t reward you with good performance once a project reaches a certain size.
Therefore, assemblies need sometimes to be split up even if deployed at the same location.
When splitting assemblies you should consider the principles of package design and you should split along feature/namespace boundaries. If you split by feature the code and the number of projects that have to be built to run a unit test is minimized; thus minimizing the time you have to wait for the test to run.
“But the framework I use already dictates me to use namespaces like view or viewmodel!”
You have two options: look for another framework or start with a namespace per feature inside the namespaces that were given by the framework. E.g. MyCompany.MyProduct.View.Theme1.Feature1
So, how do you structure your code? And why? Let me know in the comments section.