The topic of properly organizing the code inside a project is often a critical issue for developers who are just starting to learn .NET. Often in the early development stages too much time is wasted trying to figure out where a new class should be created, or when a new project should be added to a solution, or even when to put code in a specific subfolder.
The truth is that there isn't actually a single correct way to organize your code.
After years of developing in .NET, I've never actually worked on two projects where code was organized in the same way. The same goes for my personal projects: with the progress of my learning path in .NET, I have always tried to progressively move towards a "standard" way of organizing the code inside a repository.
Despite the differences, the analysis of the structure of numerous open source repositories shows that they actually share some common patterns and practices when it comes to organizing the code.
This article illustrates some of these common practices in organizing .NET projects, to help you structure your applications in a cleaner and easily understandable way.
Folder structure
.NET is actually very flexible in how you can organize the folder structure for an application. However, having a clear folder structure helps new contributors understand where to find the code they need. Even people who have never worked on your application would know where to look to find the documentation, or unit tests, or source code.
While there is no official standard on how to organize files and folders in a .NET application, many repositories follow a structure similar to the one outlined by David Fowler. A simplified version of the structure can be represented as follows:
$/
artifacts/
build/
docs/
packages/
samples/
src/
tests/
.editorconfig
.gitignore
.gitattributes
LICENSE
NuGet.Config
README.md
{solution}.sln
...
Files such as the solution file, README or LICENSE are put on the root folder, while the rest of the content is organized inside some main subfolders:
- src - Contains the actual source code for the application (the various projects with their classes etc.)
- tests - Contains unit test projects
- docs - Contains documentation files
- samples - This is where you would place files that provide examples to users on how to use your code or library
- artifacts - Build outputs go here
- packages - Contains NuGet packages
- build - Contains build customization scripts
Notice that in some cases the same structure could be recursively applied to the individual projects of the solution (for instance, you could have a src and test folder for each project).
Naming conventions for projects and namespaces
From the name of a project it should be easy to understand the functionality of the code it contains. For instance, if a project contains math related operations, it would probably contain Math in its name.
Talking about naming conventions, there isn't an actual standard; some common practices include:
- CompanyName.ProductName.Component (eg. MyCompany.MyApplication.DataAccess)
- CompanyName.Component (eg. MyCompany.Math)
- ProductName.Component (eg. MyApplication.Models)
Once you've chosen your particular naming convention, it's important to remain consistent with it.
In the case of subfolders within the same project, the namespaces of the files within the subfolders reflect the same hierarchical structure. For example, in a MyApplication.DataAccess project, classes within a SqlServer folder would belong to a MyApplication.DataAccess.SqlServer namespace.
Use multiple projects to separate macro functionality or layers
A common mistake is to put too many different functionalities inside the same project in a .NET solution. This often leads, for instance, to situations where data access logic is mixed up with UI functionalities, and there isn't a clear separation between the two areas. Instead, separating the code into multiple projects within the same solution helps you keep the code cleanly sorted in multiple distinct logical groupings; think of this like putting your dishes in a separate compartment from the cutlery. This makes it easier to understand where to look for what you need, and where the new class you're writing should be created.
One way to group code into multiple projects is by major functionality. For example, everything related to math operations in an application could go in its own project, separate from UI or data access components.
Another common approach is to divide the application into layers, creating a separate project for each layer. This helps you manage dependencies and avoid circular references, so that each layer is visible only to the correct projects. As an example, think of creating a project for the UI layer, that references the Business Logic layer, that in turn references the Data access layer; this creates a clear separation between those different areas of the application.
Within the same project it is then possible to create additional groupings, putting classes and components with similar functionalities into subfolders. Returning to the previous analogy, it would be like dividing forks and spoons into separate sections within the same compartment. For example, a data access project might have a subfolder containing all implementations related to SQL databases, a subfolder for mock implementations, and so on.
The resulting folder structure could look like this:
$/
MyApplication.sln
src/
MyApplication.UI/
MyApplication.UI.csproj
MyApplication.Math/
MyApplication.Math.csproj
MyApplication.Business/
MyApplication.Business.csproj
MyApplication.DataAccess/
SqlServer/
SqlServerRepository.cs
Mock/
MockRepository.cs
IRepository.cs
MyApplication.DataAccess.csproj
Unit tests organization
The structure and naming convention of unit test projects should reflect those of the source code that it's being tested, usually with an addition of a Tests suffix to the names of projects and classes.
For instance, the MyApplication.Math project would be tested by a unit test project called MyApplication.Math.Tests; the unit tests for a class called Calculator would be found in a test class named CalculatorTests.
$/
MyApplication.sln
src/
MyApplication.Math/
Calculator.cs
MyApplication.Math.csproj
Utilities/
MathUtilities.cs
tests/
MyApplication.Math.Tests/
CalculatorTests.cs
MyApplication.Math.Tests.csproj
Utilities/
MathUtilitiesTests.cs
Summary
This article covered some common practices in organizing code for .NET projects. While there is no single true correct approach and every project is unique, these guidelines can help you clear up some confusion when starting to build a new application in .NET.