project-structure
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseProject Structure
项目结构
Core Principles
核心原则
- Central package management — Use to manage NuGet package versions in one place. No version numbers in individual
Directory.Packages.propsfiles..csproj - Shared build properties — Use for common settings (target framework, nullable, implicit usings). Don't repeat in every project.
Directory.Build.props - .slnx for solutions — The new XML-based solution format is cleaner and more merge-friendly than the legacy format.
.sln - src/tests separation — Source projects in , test projects in
src/. Clear boundary.tests/
- 集中包管理 — 使用在一处管理NuGet包版本。单个
Directory.Packages.props文件中不包含版本号。.csproj - 共享构建属性 — 使用配置通用设置(目标框架、可空引用、隐式using)。无需在每个项目中重复配置。
Directory.Build.props - 使用.slnx管理解决方案 — 新的基于XML的解决方案格式比传统格式更简洁,且更易于合并。
.sln - 源码与测试分离 — 源码项目放在目录,测试项目放在
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.csprojMyApp/
├── 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.csprojDirectory.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
命名规范
| Element | Convention | Example |
|---|---|---|
| Solution | | |
| Project | | |
| Namespace | Matches folder path | |
| Feature folder | PascalCase, plural | |
| Test project | | |
| 元素 | 规范 | 示例 |
|---|---|---|
| 解决方案 | | |
| 项目 | | |
| 命名空间 | 与文件夹路径匹配 | |
| 功能文件夹 | 大驼峰式、复数形式 | |
| 测试项目 | | |
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
不要混合源码与测试项目
undefinedundefinedBAD — 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/
undefinedsrc/
MyApp.Api/
tests/
MyApp.Api.Tests/
undefinedDecision Guide
决策指南
| Scenario | Recommendation |
|---|---|
| New solution | |
| Package version management | |
| Shared build settings | |
| SDK version pinning | |
| Common using directives | Global usings in |
| Small API (1-2 devs) | Single project ( |
| Medium API (3-5 devs) | 2-3 projects ( |
| Large / modular app | Module-per-project with shared |
| 场景 | 推荐方案 |
|---|---|
| 新建解决方案 | 使用 |
| 包版本管理 | 使用 |
| 共享构建设置 | 使用 |
| SDK版本固定 | 使用 |
| 通用using指令 | 在 |
| 小型API(1-2名开发) | 单项目结构( |
| 中型API(3-5名开发) | 2-3个项目( |
| 大型/模块化应用 | 按模块拆分项目,并共享 |