When you begin a project, the desire is strong to keep your precious, fresh-from-the-template solution forever neat and tidy..
But, as far as tutorials go, the authors throw organization to the wind for the sake of brevity, leaving you to fend for yourself.
What guidelines should you follow for organizing projects, folders, files, and even namespaces, so that your projects don’t grow into a coiling and belching 12-headed serpent down the line?
How do we achieve the dream of clean and scalable project organization?
Unfortunately, I’ve never built a from-scratch project that’s grown to more than 50 or so files, but I have worked on a number of very large (10,000+ file, 20+ project .net solution) projects, and I have never felt that their organization was in any way a problem.
With that, I can offer a set of my own strategies for keeping projects tidy, and you can take from it what you like. Organization is somewhat of a personal thing, after all.
The Whys of Organization
First, it’s important to think about why organization is important for you and your team so you aren’t just organizing for the sake of organizing.
The main reason I can think of is a very human one.
We do it for each other.
Compilers these days generally couldn’t give a rat’s ass where any individual file is or what it’s named, so organization is for us.
But what do we gain?
With Visual Studio’s search features like “Find all references” and “Go to definition”, we don’t depend on organization as much as we used to for finding things, but there are still plenty of times you’ll find yourself looking for something that requires scanning the folder hierarchy.
A project’s structure is also a great way to help someone understand the application’s architecture without having to actually be on the dev team. The same goes for naming, it’s all for your fellow developers so that the app can continue to grow in a cohesive way.
What are some other reasons organization is important? Tweet me them!
With those whys in mind, we can follow with a couple important attitude adjustments.
1.) Because it’s mostly for our benefit, it’s not that important, so don’t obsess about it.
Sometimes I can get enraged when someone uses the “wrong” channel in slack by talking about a subject where another much more suitable channel exists. I won’t get into what childhood insanity caused such a quirk, but I can definitely over-organize. You could spend days arguing with someone about where something should go, and the benefits are not worth more than a minute of careful thought.
2.) Remember that you can always make adjustments later. You know that thing called “refactoring”? Yeah, that exists, you can move and rename stuff any time you want. If it’ll give you some peace of mind, set up a reminder every week or two to look over your project and make some organizational adjustments.
Strategies for project organization
Before I dive into how I personally go about organization, there’s some mindsets you should consider so that you don’t just blindly accept my way as king.
Consistency is what matters most
The fastest way your project will get out of control is if you or your team try to change it too often or don’t agree on a convention and stick to it. Decide on a general strategy that you like, and get your team on board. There is no optimal strategy.
If you’re on a team, you’re going to need to compromise. One of my teammates prefers camelCase for naming, and wanted to use that instead of using dashed-names for our Azure resources. You may have to do some convincing and compromising to find a solution, but it’s very important that you do.
Most developers worth their salt will observe the current conventions of the codebase and try to match them. So start now and start early.
The Public Library Analogy
In the wild, I’ve noticed two main organizational paradigms.
One is where you group things based on their type. All your middleware in one folder, all your widgets in another. This can work because, hey, if you’re looking for a widget, it’s going to be in the widget folder.
The other is what I’m calling the public library analogy where you group things based on the related application areas. Think about walking into a library or bookstore. You first look around for the overlying topic of the book you want: fiction vs. non-fiction, history, self-help, etc. From there it breaks down into sub-categories like U.S. History and Russian History. It could break down into further sub-topics, but you get the idea.
My preferred way is a blending of the two.
If you can, put your file around the area that it’s used. If this file holds some UI code related to making a credit-card payment, well perhaps you’ve got a folder for all payment related user activity, it probably makes sense that this code go there, near adjacent UI code.
Sometimes, at the lowest level, I then like to take the widget strategy where I put all widgets in the same folder, even if they aren’t that related. For example, in a recent API project, I felt it was fine to put all the DTOs in one folder, and all the middleware filters in another folder. Sometimes it’s just a feeling, go with it and adjust as needed, but be consistent.
Because I spend most of my time in the .net world, we’ll start at the solution level. If you aren’t familiar with .net, solutions are the outermost level of a project, containing all the submodules of an application.
Starting a brand new application is when you’ll be choosing a solution name for the first time. A standard strategy for that is just to name the solution after the name of the app (keep reading before you take that route). Does your new budgeting app have a doofy name like “Budgetly”? You could name your solution “Budgetly”. It wouldn’t be a big deal, you could always change it, but hear me out:
If someone from marketing wants to change the name, you may have to make some code changes for consistency. The same goes if you sell your application to someone who wants a branding change. This edge case is hot on my mind because a branding change was made on an app I’m working on and the original developers had hardcoded and laced the application name in almost every file in the 8,000+ file project.
Pro Tip: Don’t hardcode the brand name anywhere in the app. Use something generic like “BudgetApp” so you never have to change code related to branding.
The projects of a solution break up your application into modular units that plug into or are used by the other projects. This is where the idea of dependencies enters the scene.
The project level of organization is usually where you will start to see common strategies like the N-tiered, layered architecture. Tutorials almost always will simply use a single project and cram everything into that since organization is an afterthought in most cases.
A common three project starting point that I like is simply a frontend project for the UI (or API if it’s headless), a data project for storage code, and a business layer project for the core application logic.
So you’d start with something like:
BudgetApp (Solution) |-- Core/Domain (Business or Domain Layer Project) |-- Data (Data Access Layer Project) |-- UI (UI Project) |-- API (API Layer Project)
As your project grows, you could make new projects if needed, or just use subfolders to break things up.
Another important strategy is to layer your project dependencies. So in the above example, you could design your code so that the UI project only depends on the Core project to manipulate objects, and the Core project only depends on the Data project in order to actually save and restore object data.
The Core project should not know or care at all about any part of the UI project.
We do this kind of the modular plug-and-play style architecture so the code can adapt to changes like being able to swap out the frontend with something shiny and new.
I’ll save further discussion on architectural decisions like that for another article, but the main point is, your projects set up the main modules between your app, so give them some consideration beyond just the types of files going in them. However, don’t spend too much time stressing about creating the most modular app possible. I’ve wasted countless nights creating deep inheritance chains for the sake of decoupling my app when in the end, the app was never going to be decoupled in the first place.
There’s a balance between caring about modularity and actually implementing the main value proposition of an application.
Folders are a great way to break up a project into related groupings of files. This isn’t much different than how you organize your home computer, unless you’re one of those people that puts everything on the desktop. Use some damn folders you slob! ;)
I like to make folder names plural if it contains a set of types like the standard
\Controllers\ in a .net Web API project for holding the controller classes.
If your folders contain even more sub-folders and perhaps represent a higher level grouping, then I might not use a plural name. For example, splitting up your UI project into the different areas of the app. Our budget app could have two main areas, one for your banking transactions, and on for the actual budget with categories and amounts. Something like below could make sense:
BudgetApp (Solution) |-- UI (UI Layer Project) | |-- Accounting (Folder) | |-- Budgeting (Folder)
Namespaces are what you use actually include into a code file to bring in a dependency. Use namespaces to further break up the distinct parts of your application.
For most cases, you could simply mimic the folder structure of your project to create namespaces. In fact, some tools in Visual Studio will even do this for you. Perhaps you’ve got some business layer code for calculating like in the below folder layout:
BudgetApp (Solution) |-- Core (Business Layer Project) | |-- Accounting | | |-- Calculators | | |-- SavingsRateCalculator | | |-- Other Calculators...
A namespace like
BudgetApp.Core.Accounting.Calculators; would be a perfectly fine starting point.
I won’t go into the code level of naming and organization in this article, because it would go on forever, but generally your files are named after the class they hold, and classes are usually named using nouns.
One class per file is most common.
Sometimes people will put a group of things like enums into a single file since enums are typically pretty small, but I still prefer the one-file-per-object strategy because enums can actually tell a big story about your application to outsiders so it’s nice to have them easily visible from the file structure.
A Slightly Larger Example
I’ve put together a sample project with stubbed out files (no implementations) for a hypothetical budget application API.
By no means is it a complete project, but demonstrates how I might start organizing projects, folders, and files within a solution, and also includes a hint at project dependencies and namespaces. Please ask me about any specific questions you might have about it, but I’ll go over the main decision points.
This project uses a basic layered project structure with a project for the API, the data access, the core (or domain) business layer. I’ve also decided to split out a separate project for models. I might have put the models as a folder in the core project, but I imagined I might share them between several layers rather than using DTOs to move data between layers. This is mostly personal preference for a small project, but sometimes DTOs work great.
A common question I’ve seen asked is:
Where do the repositories live?
I’ve chosen to put them in the data access layer because they act as the front fascia for data storage, and my intent was to have them be used only by the Core layer so that my API layer didn’t have to know anything about that.
If I wanted repositories to be used in the API layer (like I’ve demonstrated in a past article), I might decide to put them in the Core layer. That’s another one of those “feeling” decisions that you don’t need to spend much time on since you can change it any time.
I’ve also split the Core project into areas for the accounting logic and the budgeting logic. Those distinctions might not be the best example if don’t use a budget, but the main differences are that accounting is for logging actual inflow and outflow of money with your bank and credit card accounts, and budgeting is all about the excel-style categorization and setting of amounts you’d like to stick to for each month.
The lowest level folders are simply plural nouns for the object types that exist in them, and the namespaces just match the folder structure.
The human element
Remembering that organization is really all about us monkeys trying to make things easier to understand for each other. How can you structure the project so it conveys the architectural and design decisions so that a stranger could get a good feel for it just by reviewing the folders?
KISS (or keep it simple, stupid), applies here. Organizing is mostly aesthetic and ease of searching. It doesn’t make the code run better.
There is no need to obsess about decisions like this when they can be changed without too much headache at any time. Start with a basic structure and adapt as your project grows.
The fastest way to a mess is being inconsistent. Get your team involved to some extent so you agree on some standards, and then do you utmost to follow them. This goes all the way down to naming variables in code.
There is no one true way. Organized means everything in it’s place, and it’s place may have already been decided by an earlier developer.
Take some time to look at other large projects on Github by people and teams you respect to get a feel for commonalities within your platform’s community. Community and platform consistency is also a good thing, again because of the human element. If you’ve already worked on asp.net projects before, it can be great for confidence and productivity if you join a new project and the project structure is already familiar.
Consider layering and modular design
I’ve shown some starting points for how I like to do things. The outermost decisions of my projects tend to reflect architectural patterns, and the innermost decisions tend to be about grouping related code.
Remember, that’s just the way I like to do it, it’s not the gospel. You will find preferential differences between you and your team (if you have one) and will need to compromise.
Take some time to look at other large projects on Github by people and teams you respect to get a feel for commonalities within your platform’s community.
Whew, that was a long one for me. Hopefully that helped you with questions about project organization.
If you’ve got a second, help someone else by copying and sharing this link.