All guides

Engineering toolbox

.NET Project Design

An opinionated solution layout for .NET: folder structure, project dependency order, naming convention, and the reasoning behind each choice.

Updated Sep 11, 202611 min read
dotnet
architecture
solution-structure
csharp

The following approach to .NET solutions is an opinionated layout that is designed to provide guidance and structure for developers working with the .NET framework. The layout is intended to be adaptable and flexible, and it covers a range of different targets including Desktop, Mobile, and Web.

The layout is based on a number of key principles, including modularity, scalability, and maintainability. It is designed to help developers create solutions that are easy to understand, test, and maintain, with a clear and consistent structure that makes it easy to navigate and understand the codebase.

The layout includes a number of different components, including a shared library for common functionality, a set of reusable controls and components, and a set of project templates for different types of projects. This allows developers to quickly get started with a new project, and to reuse and extend existing code.

The layout also includes practices for working with different types of projects, such as web applications, desktop applications, and mobile applications. This includes guidance on how to structure the code, how to handle user interface and user experience, and how to handle different types of data and storage.

Overall, the opinionated layout and approach to .NET solutions is designed to provide developers with a clear and consistent structure for building solutions, while also allowing them to quickly get started with new projects and to reuse and extend existing code.

  • πŸ“‚ Assets - All assets related to the solution.
    • πŸ“‚ Images - Source for images.
      • πŸ“œ Default.ico
      • πŸ“œ Default.png
  • πŸ“‚ Documentation - Project Wiki documentation.
    • πŸ“‚ Licenses
  • πŸ“‚ Source - Project source for the solution
    • πŸ“‚ {ProjectName}.Android - Bootstrap Entry Point for Droid Applications.
      • πŸ“œ MainActivity.cs
    • πŸ“‚ {ProjectName} - Bootstrap Entry Point for the Application.
      • πŸ“œ Program.cs
    • πŸ“‚ {ProjectName}.Console - Bootstrap Entry Point for the Application.
      • πŸ“œ Program.cs
    • πŸ“‚ {ProjectName}.Core - All Business Logic related to the Application.
      • πŸ“‚ Extensions
      • πŸ“‚ Utils
      • πŸ“‚ Tasks
      • πŸ“‚ ViewModels
    • πŸ“‚ {ProjectName}.Components - Any Plugin-Style logic or 3rd Party logic. e.g. Fluent Scheduler Tasks
      • πŸ“‚ Jobs
    • πŸ“‚ {ProjectName}.Data - Code related to data internally visible, access via {ProjectName}.Kit interfaces.
      • πŸ“‚ Extensions
      • πŸ“‚ Entities
      • πŸ“‚ Models
      • πŸ“‚ Migrations
      • πŸ“‚ Services
      • πŸ“œ DBContext.cs
    • πŸ“‚ {ProjectName}.Hosting - Core run-time and setup of Application Features and Services.
      • πŸ“œ {ProjectName}.conf
    • πŸ“‚ {ProjectName}.iOS- Bootstrap Entry Point for iOS Applications.
      • πŸ“œ AppDelegate.cs
    • πŸ“‚ {ProjectName}.Kit - Interoperability, holds all interfaces and candidates for Nuget Package.
      • πŸ“‚ Abstractions
      • πŸ“‚ Attributes
      • πŸ“‚ Data
        • πŸ“‚ Entities
        • πŸ“‚ Models
      • πŸ“‚ Extensions
      • πŸ“œ AppSettings.cs
      • πŸ“œ Constants.cs
    • πŸ“‚ {ProjectName}.Test - All Integration and Unit Tests.
      • πŸ“‚ Extensions
      • πŸ“‚ Integration
      • πŸ“‚ Unit
    • πŸ“‚ {ProjectName}.Function - All logic related to the operation of Azure Functions.
      • πŸ“œ Startup.cs
    • πŸ“‚ {ProjectName}.Web - All logic related to the operation of Web Sites and APIs.
      • πŸ“‚ _build
        • πŸ“œ postbuild.cmd
        • πŸ“œ postbuild.sh
      • πŸ“‚ wwwroot - All source related to the Web Site logic and presentation.
        • πŸ“‚ css
        • πŸ“‚ img
        • πŸ“‚ js
        • πŸ“œ favicon.ico
      • πŸ“‚ Api
      • πŸ“‚ Components
      • πŸ“‚ Extensions
      • πŸ“‚ Hubs
      • πŸ“‚ Middleware
      • πŸ“‚ Pages
      • πŸ“‚ Shared
      • πŸ“‚ Themes
      • πŸ“‚ Views
        • πŸ“‚ Shared
      • πŸ“‚ ViewComponents
      • πŸ“œ appsettings.Development.json
      • πŸ“œ appsettings.json
      • πŸ“œ Startup.cs
    • πŸ“‚ {ProjectName}.Services - Logic that requires a lifecycle and intended for Dependency Injection.
    • πŸ“‚ {ProjectName}.Feature - Logic that requires configuration and intended for Dependency Injection.
    • πŸ“œ .editorconfig - provides consistent coding styles.
    • πŸ“œ CodeCoverage.runsettings
    • πŸ“œ CommonAssemblyInfo.cs - _Global Assembly
    • πŸ“œ Default.ico
    • πŸ“œ Default.png
    • πŸ“œ Directory.Build.props - Defines global Project options.
    • πŸ“œ Dockerfile
    • πŸ“œ GlobalSuppressions.cs
    • πŸ“œ NuGet.Config - XML file for controlling NuGets behaviour.
    • πŸ“œ Product.props
    • πŸ“œ Version.props
    • πŸ“œ xunit.runner.json - use to configure xUnit.net execution
    • πŸ“œ stylecop.json - if Stylecop is in use.
    • πŸ“œ {ProjectName}.sln
  • πŸ“‚ Tools - External code required to support the operation of the Solution.
    • {ProjectName}.Benchmark - Runtime support for BenchmarkDotNet.
    • {ProjectName}.Migration - Source and scripts for Schema Migration e.g. EF Core.
  • πŸ“œ .dockerignore
  • πŸ“œ .gitattributes
  • πŸ“œ .gitignore
  • πŸ“œ CODE_OF_CONDUCT.md
  • πŸ“œ CONTRIBUTING.md
  • πŸ“œ contributors.txt
  • πŸ“œ copyright.txt
  • πŸ“œ PULL_REQUEST_TEMPLATE.md
  • πŸ“œ azure-pipeline.yml
  • πŸ“œ README.md

The following is a list of various components that may be included in a .NET solution, organized by naming conventions. Each component is prefixed with the project name, providing a clear association with the project and easy identification.

  1. {ProjectName}.Kit - This component may include various tools and utility functions that are commonly used throughout the project.
  2. {ProjectName}.Data - This component may include classes, interfaces, and other types that are used to handle data storage, retrieval, and management.
  3. {ProjectName}.Core - This component may include the core logic and functionality of the project.
  4. {ProjectName}.Components - This component may include reusable controls, widgets, or other components that are used throughout the project.
  5. {ProjectName}.Features - This component may include different features or functionalities of the project, grouped by functionality.
  6. {ProjectName}.Services - This component may include classes, interfaces, and other types that are used to handle services, such as APIs or external libraries.
  7. {ProjectName}.Web - This component may include classes, interfaces, and other types that are specific to web applications or web-based features.
  8. {ProjectName}.Function - This component may include classes, interfaces, and other types that are specific to cloud functions or serverless architecture.
  9. {ProjectName}.Hosting - This component may include classes, interfaces, and other types that are specific to hosting, deployment and environment management.
  10. {ProjectName}.Console - This component may include classes, interfaces, and other types that are specific to console applications or command-line interfaces.
  11. {ProjectName}.iOS - This component may include classes, interfaces, and other types that are specific to iOS mobile applications.
  12. {ProjectName}.Android - This component may include classes, interfaces, and other types that are specific to Android mobile applications.
  13. {ProjectName} - This component may include the main entry point of the project, such as the main class or application class.
  14. {ProjectName}.Test - This component may include test classes, test cases, and other types that are used for testing and verifying the functionality of the project.

The approach to solution and project naming provided is a consistent and clear naming convention. The convention is broken down into two parts:

{Entity}.{Project}.{Topic} - This format is used for solutions or projects that are associated with a specific entity, such as a company. For example, "Company.Thingo.Data" would be the naming convention for a data project associated with the company "Thingo".

{Project}.{Topic} - This format is used for solutions or projects that are not associated with a specific entity. For example, "Product.Kit" would be the naming convention for a kit project that is not associated with a specific company.

This naming convention allows for easy identification of the solution or project's purpose, its association with an entity, and its topic of focus. The naming convention is easy to understand and follow, which can help to improve the organization and maintainability of the solution or project.

code
Company.Thingo.Kit
Company.Thingo.Data
Company.Thingo.Core
Company.Thingo.Features
Company.Thingo.Services
Company.Thingo.Web
Company.Thingo.Hosting
Company.Thingo.Test

When it comes to organizing code in a .NET solution, one approach is to have separate "src" and "test" folders to keep the source code and test code separate. However, there are several reasons why this approach may not be the best option.

One reason is that in order for Directory.Build.props to have an effect, projects need to be parented to a directory containing the .props files.1 This can cause side effects with tools such as BenchmarkDotNet, as they may not be able to find the necessary files in the expected location.

Another reason is that there is no requirement to have separate folders for source code and test code, as .NET does not consider folder structures as part of the namespace convention. This means that having separate folders for source and test code can lead to redundancy and unnecessary complexity in the project structure.

Additionally, having all the code in the same folder structure allows for a more streamlined and simplified organization, making it easier for developers to navigate and understand the project as a whole. This can help improve maintainability and ease of development.

Overall, while having separate "src" and "test" folders may seem like a logical way to organize code, there are several reasons why it may not be the best approach, including issues with tools and the lack of a requirement for separate folders in .NET projects.

The Clean Architecture approach suggests having an "Infrastructure" folder that holds everything like Data and Services.2 This approach may seem logical as it would group similar functionality together in one location. However, there are several reasons why this approach may not be the best option.

One reason is that it diminishes the "Separation of Concern" principle. The infrastructure folder would hold both the data and service layers, which may lead to a blurring of responsibilities and make it harder to understand the different concerns and responsibilities of each layer.

Another reason is that it clumps logic together, making it difficult to understand the different responsibilities of each layer. This can make it harder to reason about the system, test it, and make changes without introducing unintended side effects.

Instead, it is recommended to have separate folders for each concern, such as one for Data, one for Services, and one for Domain. This allows for a clear separation of concerns and makes it easy to understand the different responsibilities of each layer. It also allows for better testability and maintainability.

In general, having a {ProjectName}.Infrastructure folder like Clean may seem logical, but it can lead to a diminishment of the "Separation of Concern" principle, and clumps logic together, making it harder to understand the different responsibilities of each layer.

When it comes to organizing code in a .NET solution, using terms like "Domain" and "src" may seem like logical choices. However, there are several reasons why this approach may not be the best option.

One reason is that the goal is to achieve high readability and support diverse audiences. Using terms like "Domain" and "src" may not be immediately understandable to all developers and stakeholders, and could lead to confusion or misunderstandings.

Another reason is that simple conventions and a reduced folder count can lead to clarity and ease of navigation. Having too many folders and subfolders can make it difficult for developers to quickly find the files and information they need, which can negatively impact productivity and efficiency.

Using more intuitive and easy to understand terms for the folder structure can help increase the accessibility of the codebase for a broad audience. This can be especially important when using tools like Azure DevOps, where readability is a key factor in making the codebase accessible to a wider range of developers and stakeholders.

In general, terms like "Domain" and "src" may seem like logical choices for organizing code, but they may not always be the best option for high readability, supporting diverse audiences, simple convention, reduced folder count and clarity.

The "Kit" term in the context of a .NET solution may refer to a specific component or module that contains a collection of abstractions, types, interfaces, attributes, and configurations. This separation of functionality helps to avoid dependency issues, and it's a good practice to code to contracts.

The "Kit" component is intended to be a collection of reusable, low-level functionality that is used throughout the project. This can include utility functions, common data structures, and other types that are used to support the core functionality of the project.

One of the key benefits of using a "Kit" component is that it promotes code reuse and makes it easy for developers to access and use common functionality. This can help to reduce development time and improve the overall maintainability of the project.

It's also worth noting that the Kit component is a good candidate for packaging, as it can encapsulate utility and feature code for wider distribution. This can be useful for sharing the functionality with other solutions and can be distributed as a NuGet package.

In general, the "Kit" component is a useful way to separate and organize functionality in a .NET solution, and it can help to improve code reuse, maintainability, and distribution.

FootnotesπŸ”—

  1. Microsoft. Customize the build by folder. Microsoft Learn. learn.microsoft.com ↩

  2. Martin, R. C. (2017). Clean Architecture: A Craftsman's Guide to Software Structure and Design. Prentice Hall. ↩

.NET Project Design | Push Manifesto Β· Push Manifesto