project-structure

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Project Structure

项目结构

Core Principles

核心原则

  1. Central package management — Use
    Directory.Packages.props
    to manage NuGet package versions in one place. No version numbers in individual
    .csproj
    files.
  2. Shared build properties — Use
    Directory.Build.props
    for common settings (target framework, nullable, implicit usings). Don't repeat in every project.
  3. .slnx for solutions — The new XML-based solution format is cleaner and more merge-friendly than the legacy
    .sln
    format.
  4. src/tests separation — Source projects in
    src/
    , test projects in
    tests/
    . Clear boundary.
  1. 集中包管理 — 使用
    Directory.Packages.props
    在一处管理NuGet包版本。单个
    .csproj
    文件中不包含版本号。
  2. 共享构建属性 — 使用
    Directory.Build.props
    配置通用设置(目标框架、可空引用、隐式using)。无需在每个项目中重复配置。
  3. 使用.slnx管理解决方案 — 新的基于XML的解决方案格式比传统
    .sln
    格式更简洁,且更易于合并。
  4. 源码与测试分离 — 源码项目放在
    src/
    目录,测试项目放在
    tests/
    目录,边界清晰。

Patterns

规范模式

Solution Layout

解决方案布局

MyApp/
├── MyApp.slnx                       # Solution file
├── Directory.Build.props             # Shared MSBuild properties
├── Directory.Packages.props          # Central package management
├── .editorconfig                     # Code style rules
├── .gitignore
├── global.json                       # SDK version pinning
├── src/
│   ├── MyApp.Api/                    # Web API (entry point)
│   │   ├── MyApp.Api.csproj
│   │   ├── Program.cs
│   │   └── Features/
│   ├── MyApp.Domain/                 # Domain entities, value objects (optional)
│   │   └── MyApp.Domain.csproj
│   └── MyApp.Infrastructure/         # EF Core, external services (optional)
│       └── MyApp.Infrastructure.csproj
└── tests/
    └── MyApp.Api.Tests/
        └── MyApp.Api.Tests.csproj
MyApp/
├── MyApp.slnx                       # 解决方案文件
├── Directory.Build.props             # 共享MSBuild属性
├── Directory.Packages.props          # 集中包管理
├── .editorconfig                     # 代码风格规则
├── .gitignore
├── global.json                       # SDK版本固定
├── src/
│   ├── MyApp.Api/                    # Web API(入口点)
│   │   ├── MyApp.Api.csproj
│   │   ├── Program.cs
│   │   └── Features/
│   ├── MyApp.Domain/                 # 领域实体、值对象(可选)
│   │   └── MyApp.Domain.csproj
│   └── MyApp.Infrastructure/         # EF Core、外部服务(可选)
│       └── MyApp.Infrastructure.csproj
└── tests/
    └── MyApp.Api.Tests/
        └── MyApp.Api.Tests.csproj

Directory.Build.props

Directory.Build.props示例

xml
<Project>
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <LangVersion>14</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
  </PropertyGroup>
</Project>
xml
<Project>
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <LangVersion>14</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
  </PropertyGroup>
</Project>

Directory.Packages.props (Central Package Management)

Directory.Packages.props(集中包管理)

xml
<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>

  <ItemGroup>
    <!-- Versions below are illustrative — resolve the current stable versions
         with `dotnet add package <name>` (no --version flag); see the packages rule -->
    <!-- ASP.NET Core -->
    <PackageVersion Include="Mediator.Abstractions" Version="3.0.0" />
    <PackageVersion Include="Mediator.SourceGenerator" Version="3.0.0" />
    <PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="12.0.0" />

    <!-- Data -->
    <PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.10" />
    <PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.10" />

    <!-- Observability -->
    <PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
    <PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.17.0" />

    <!-- Testing -->
    <PackageVersion Include="xunit.v3" Version="3.2.2" />
    <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.10" />
    <PackageVersion Include="Testcontainers.PostgreSql" Version="4.13.0" />
  </ItemGroup>
</Project>
xml
<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>

  <ItemGroup>
    <!-- 以下版本仅作示例 — 使用`dotnet add package <name>`(无需--version参数)获取当前稳定版本;参考包管理规则 -->
    <!-- ASP.NET Core -->
    <PackageVersion Include="Mediator.Abstractions" Version="3.0.0" />
    <PackageVersion Include="Mediator.SourceGenerator" Version="3.0.0" />
    <PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="12.0.0" />

    <!-- 数据相关 -->
    <PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.10" />
    <PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.10" />

    <!-- 可观测性 -->
    <PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
    <PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.17.0" />

    <!-- 测试相关 -->
    <PackageVersion Include="xunit.v3" Version="3.2.2" />
    <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.10" />
    <PackageVersion Include="Testcontainers.PostgreSql" Version="4.13.0" />
  </ItemGroup>
</Project>

Project File (.csproj) with Central Package Management

启用集中包管理的项目文件(.csproj)

xml
<Project Sdk="Microsoft.NET.Sdk.Web">
  <!-- No TargetFramework here — inherited from Directory.Build.props -->

  <ItemGroup>
    <!-- No Version attribute — managed centrally -->
    <PackageReference Include="Mediator.Abstractions" />
    <PackageReference Include="Mediator.SourceGenerator" />
    <PackageReference Include="FluentValidation.DependencyInjectionExtensions" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" />
    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
    <PackageReference Include="Serilog.AspNetCore" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\MyApp.Domain\MyApp.Domain.csproj" />
    <ProjectReference Include="..\MyApp.Infrastructure\MyApp.Infrastructure.csproj" />
  </ItemGroup>
</Project>
xml
<Project Sdk="Microsoft.NET.Sdk.Web">
  <!-- 此处无需指定TargetFramework — 从Directory.Build.props继承 -->

  <ItemGroup>
    <!-- 无需Version属性 — 由集中配置管理 -->
    <PackageReference Include="Mediator.Abstractions" />
    <PackageReference Include="Mediator.SourceGenerator" />
    <PackageReference Include="FluentValidation.DependencyInjectionExtensions" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" />
    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
    <PackageReference Include="Serilog.AspNetCore" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\MyApp.Domain\MyApp.Domain.csproj" />
    <ProjectReference Include="..\MyApp.Infrastructure\MyApp.Infrastructure.csproj" />
  </ItemGroup>
</Project>

global.json (SDK Pinning)

global.json(SDK版本固定)

json
{
  "sdk": {
    "version": "10.0.100",
    "rollForward": "latestFeature"
  }
}
json
{
  "sdk": {
    "version": "10.0.100",
    "rollForward": "latestFeature"
  }
}

.slnx Solution Format

.slnx解决方案格式

xml
<Solution>
  <Folder Name="/src/">
    <Project Path="src/MyApp.Api/MyApp.Api.csproj" />
    <Project Path="src/MyApp.Domain/MyApp.Domain.csproj" />
    <Project Path="src/MyApp.Infrastructure/MyApp.Infrastructure.csproj" />
  </Folder>
  <Folder Name="/tests/">
    <Project Path="tests/MyApp.Api.Tests/MyApp.Api.Tests.csproj" />
  </Folder>
</Solution>
xml
<Solution>
  <Folder Name="/src/">
    <Project Path="src/MyApp.Api/MyApp.Api.csproj" />
    <Project Path="src/MyApp.Domain/MyApp.Domain.csproj" />
    <Project Path="src/MyApp.Infrastructure/MyApp.Infrastructure.csproj" />
  </Folder>
  <Folder Name="/tests/">
    <Project Path="tests/MyApp.Api.Tests/MyApp.Api.Tests.csproj" />
  </Folder>
</Solution>

Naming Conventions

命名规范

ElementConventionExample
Solution
CompanyName.AppName
or
AppName
MyApp.slnx
Project
AppName.Layer
MyApp.Api
,
MyApp.Domain
NamespaceMatches folder path
MyApp.Api.Features.Orders
Feature folderPascalCase, plural
Features/Orders/
Test project
ProjectName.Tests
MyApp.Api.Tests
元素规范示例
解决方案
CompanyName.AppName
AppName
MyApp.slnx
项目
AppName.Layer
MyApp.Api
,
MyApp.Domain
命名空间与文件夹路径匹配
MyApp.Api.Features.Orders
功能文件夹大驼峰式、复数形式
Features/Orders/
测试项目
ProjectName.Tests
MyApp.Api.Tests

Anti-patterns

反模式

Don't Scatter Package Versions

不要分散包版本

xml
<!-- BAD — version in every .csproj, version drift -->
<PackageReference Include="Mediator.Abstractions" Version="2.0.0" />  <!-- in Project A -->
<PackageReference Include="Mediator.Abstractions" Version="3.0.0" />  <!-- in Project B -->

<!-- GOOD — central management, one version -->
<!-- Directory.Packages.props: <PackageVersion Include="Mediator.Abstractions" Version="3.0.0" /> -->
<!-- .csproj: <PackageReference Include="Mediator.Abstractions" /> -->
xml
<!-- 错误示例 — 每个.csproj都有版本号,易出现版本不一致 -->
<PackageReference Include="Mediator.Abstractions" Version="2.0.0" />  <!-- 项目A中 -->
<PackageReference Include="Mediator.Abstractions" Version="3.0.0" />  <!-- 项目B中 -->

<!-- 正确示例 — 集中管理,统一版本 -->
<!-- Directory.Packages.props: <PackageVersion Include="Mediator.Abstractions" Version="3.0.0" /> -->
<!-- .csproj: <PackageReference Include="Mediator.Abstractions" /> -->

Don't Repeat Build Properties

不要重复构建属性

xml
<!-- BAD — same properties in every .csproj -->
<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
  <Nullable>enable</Nullable>
  <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<!-- GOOD — once in Directory.Build.props, inherited everywhere -->
xml
<!-- 错误示例 — 每个.csproj都重复相同属性 -->
<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
  <Nullable>enable</Nullable>
  <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<!-- 正确示例 — 在Directory.Build.props中配置一次,所有项目继承 -->

Don't Mix Source and Test Projects

不要混合源码与测试项目

undefined
undefined

BAD — tests mixed with source

错误示例 — 测试项目与源码混合

src/ MyApp.Api/ MyApp.Api.Tests/ # test project in src/
src/ MyApp.Api/ MyApp.Api.Tests/ # 测试项目放在src/目录

GOOD — clear separation

正确示例 — 清晰分离

src/ MyApp.Api/ tests/ MyApp.Api.Tests/
undefined
src/ MyApp.Api/ tests/ MyApp.Api.Tests/
undefined

Decision Guide

决策指南

ScenarioRecommendation
New solution
.slnx
format
Package version management
Directory.Packages.props
(central)
Shared build settings
Directory.Build.props
SDK version pinning
global.json
Common using directivesGlobal usings in
Directory.Build.props
Small API (1-2 devs)Single project (
MyApp.Api
)
Medium API (3-5 devs)2-3 projects (
Api
,
Domain
,
Infrastructure
)
Large / modular appModule-per-project with shared
Contracts
场景推荐方案
新建解决方案使用
.slnx
格式
包版本管理使用
Directory.Packages.props
(集中管理)
共享构建设置使用
Directory.Build.props
SDK版本固定使用
global.json
通用using指令
Directory.Build.props
中配置全局using
小型API(1-2名开发)单项目结构(
MyApp.Api
中型API(3-5名开发)2-3个项目(
Api
Domain
Infrastructure
大型/模块化应用按模块拆分项目,并共享
Contracts
项目