This document details the Nerdery standard for solution structure when creating a new application.
Module - the organizational unit for a functionally distinct area of a solution. MUST be organized in a folder or a project of it’s own.
A separate solution SHOULD be created for each deployable part of the application, for example, if a project has multiple APIs, each API should have it's own solution. If there is a need to share code between solutions (note: this induces an external coupling that you should not introduce lightly), this should be done using versioned NuGet packages.
A solution MAY contain multiple, deployable components IF the number of those components are small and complete. For example, you MAY create a solution that contains a Web Application and an Authorization Application if those applications can be deployed together.
Solutions SHOULD follow the naming convention: Client.Application.Component
e.g. Acme.Explosives.OrderApi
If a Solution combines several components a more generic name MAY be used.
e.g. Acme.Application solution that contains Web, Core, and ScheduledTasks projects.
The same name SHOULD be used for
- Solution Name
- Solution Folder Name
- Project Name (if a single component)
- Project Namespace (if a single component)
- Assembly Name (if a single component)
- Assembly File Name (if a single component)
- Root Namespace
Projects SHOULD follow the naming convention: Client.Application.Component.Module
e.g. Acme.Explosives.OrderApi.Core
If a solution combines several components a more generic name MAY be used.
e.g. Acme.Explosives.Core project that contains core behavior for several components.
The same name SHOULD be used for
- Project Name
- Project Folder Name
- Project Namespace
- Assembly Name
- Assembly File Name
- SHOULD be created using .NET Core
- SHOULD use feature folders
- All code SHOULD be kept within a single project structure
- If a project contains both interfaces and implementation these SHOULD kept together. If there is a single implementation for an interface they SHOULD be kept in the same file with the interface at the top and the implementation following it. If there are multiple implementations, consider creating a folder for the module and putting the interface and implementation in it.
- .NET namespaces MUST mirror the folder structure in which classes in that
namespace are located. For example:
Client.Project.Data.Sql.SqlParser
resides on the pathData\Sql\SqlParser.cs
within theClient.Project
root. - Migrations (see: Data Access Patterns)
- Docs (see: Project Documentation)
The general code structure MAY consist of the following modules. These SHOULD be handled as areas/folders in your solution until they need to be shared with additional components of your solution. For larger solutions containing multiple deployable components, you MAY start by creating them as separate projects.
This is not an exhaustive list. This is a list of items that are present in common applications. If additional project types arise that do not fit in one of these categories, additional folders and/or projects MAY be created.
The modules below are listed in reverse order of dependency, modules MUST NOT depend on a module listed above it. In the case where modules may reside in the same project care MUST be taken to maintain this rule. For separate projects a reference MUST NOT be made that violates this rule. Each module is detailed below.
- Web
- Api - a suitably named API, ex. OrdersApi, ProductsApi
- Services
- Data
- Domain
- Core
We RECOMMEND enforcing dependency constraints with a tool such as NetArchTest (pkg) in a unit test project.
- Project: Client.Application
- Folder: Core
- Folder: Domain
- Folder: Data
- Project: Client.Application.Test
- Folder: Core
- Folder: Domain
- Folder: Data
- Project: Client.Application.Domain
- Project: Client.Application.Domain.Test
- Project: Client.Application.Core
- Project: Client.Application.Core.Test
The Core
namespace SHOULD contain all functionality that is not expansive
enough to warrant a project of it’s own, but are required by multiple consumers
(e.g. Application and API).
- Logging
- Generic helper classes for logging
- Profiling
- Generic helper classes for profiling
- .NET Framework Extensions, Reflection Helpers
- Items expanding core functionality of the .NET framework.
- Extension methods
- Domain Objects (POCO) implementing the terms of art for the solution
- Business logic scoped only to the above objects
- Validators
- Filters
- Factories
- Regular Expressions
- Shared data transfer objects
- Reference to persistence libraries
- Entity Framework Fluent API mapping of Domain objects
- Clients for APIs
See Also
The Services
module contains business logic for the application. This includes
consumption of lower layers (e.g. Data
/ Domain
), as well as custom logic for
other portions of the application to use. Services SHOULD be the main area where
business logic appears. Services SHOULD also be the lowest level in which
outside integrations occur.
This module MAY be split into more than one project or module for large solutions, dependency management, or to facilitate multiple developer teams.
Each service class within the Services module MUST have an interface.
- Business logic
- Integrations with external services
- Connection of lower components
- Examples may be: thin layer for retrieving and storing Data objects (Query object)
The API module MAY or MAY NOT exist, depending on the application.
If the application needs to support external users of the data and services, an API SHOULD be used. If you have multiple APIs you SHOULD implement each in a separate solution so that it is independently deployable. See the standards on API Communication for more information.
API MAY be used for AJAX.
- API for external consumers
- It is RECOMMENDED that the API be RESTful
- (optional) AJAX calls
- UI (Razor)
- Authentication
- Configuration
- Assets - for .NET Core this should be in wwwroot
- Attributes
- Features (you MAY omit the subfolders)
- Controllers
- Models
- Views
- Docs
- Logging
- Models (shared models)
- Resources
- Util
- Views (shared views)
- Shared
Web and API MAY be combined into a single project for smaller applications or for applications where the hosting environment is limited (e.g. A single azure machine is needed to host both Web and API sites).
- Module tests MUST reside in an organizational unit (folder or project) mirroring the module to be tested. The project containing unit tests MUST use a namespace ending in "Tests". The project containing integration tests MUST use a namespace ending in "IntegrationTests".
- Test projects MUST NOT contain a mixture of unit and integration tests. Mixing test types makes it difficult to use these tests at different stages of the CI/CD pipeline.
See the standards on unit testing for more information.
References