project-structure について
.NET チーム向けに、ソリューション構成とビルド設定を整えるスキルです。共有 MSBuild 設定、NuGet バージョンの一元管理、SDK 固定、再現可能なビルド、リリースバージョン管理を扱います。
主な機能
- 01ソリューションとプロジェクトを構成
- 02MSBuild プロパティを一元管理
- 03NuGet バージョンを一元管理
- 04global.json で SDK を固定
- 05決定的ビルドを構成
- 06リリースノートからバージョンを管理
活用シーン
- 01新しい .NET ソリューションを構築
- 02複数プロジェクトの設定を統一
- 03パッケージのバージョンを統一
- 04CI の復元とビルドを再現可能にする
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill project-structureインストール後、Claude Code で/project-structureを入力して読み込まれたか確認します。
詳細
.NET プロジェクトの構成とビルド設定
このスキルを使用するタイミング
以下の場合にこのスキルを使用します。
- 最新のベストプラクティスに沿って新しい .NET ソリューションをセットアップする
- 複数のプロジェクトに適用するビルドプロパティを一元管理する
- パッケージバージョンの一元管理を導入する
- デバッグと NuGet パッケージ向けに SourceLink をセットアップする
- リリースノートを使ってバージョン管理を自動化する
- 一貫したビルドのために SDK バージョンを固定する
推奨するソリューション構成
MyApp/
├── .config/
│ └── dotnet-tools.json # Local .NET tools
├── .editorconfig
├── .gitignore
├── global.json
├── nuget.config
├── Directory.Build.props
├── Directory.Build.targets
├── Directory.Packages.props
├── MyApp.slnx # .NET 9+ SDK / VS 17.13+
├── src/
│ ├── MyApp.Core/
│ │ └── MyApp.Core.csproj
│ ├── MyApp.Api/
│ │ ├── MyApp.Api.csproj
│ │ ├── Program.cs
│ │ └── appsettings.json
│ └── MyApp.Infrastructure/
│ └── MyApp.Infrastructure.csproj
└── tests/
├── MyApp.UnitTests/
│ └── MyApp.UnitTests.csproj
└── MyApp.IntegrationTests/
└── MyApp.IntegrationTests.csproj
主な原則:
src/ディレクトリとtests/ディレクトリを分ける- 関心事ごとにプロジェクトを分ける(Core/Domain、Infrastructure、API/Host)
- ソリューションファイルをリポジトリのルートに配置する
- 共通のビルド設定をすべてリポジトリのルートに配置する
ソリューションファイルの形式
.slnx(最新 — .NET 9+)
XML ベースのソリューション形式で、人が読みやすく、差分の確認に適しています。.NET 9+ SDK または Visual Studio 17.13+ が必要です。
<Solution>
<Folder Name="/build/">
<File Path="Directory.Build.props" />
<File Path="Directory.Packages.props" />
<File Path="global.json" />
<File Path="NuGet.Config" />
</Folder>
<Folder Name="/src/">
<Project Path="src/MyApp.Core/MyApp.Core.csproj" />
<Project Path="src/MyApp.Api/MyApp.Api.csproj" />
<Project Path="src/MyApp.Infrastructure/MyApp.Infrastructure.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/MyApp.UnitTests/MyApp.UnitTests.csproj" />
<Project Path="tests/MyApp.IntegrationTests/MyApp.IntegrationTests.csproj" />
</Folder>
</Solution>
.sln から .slnx への移行
dotnet sln MySolution.sln migrate
重要: 同じリポジトリに .sln ファイルと .slnx ファイルの両方を残さないでください。
新しい .slnx ソリューションの作成
# .NET 10+: Creates .slnx by default
dotnet new sln --name MySolution
# .NET 9: Specify the format explicitly
dotnet new sln --name MySolution --format slnx
dotnet sln add src/MyApp/MyApp.csproj
メリット
- マージの競合を大幅に減らせます
- 人が読みやすく、編集できます
- 最新の
.csproj形式との一貫性があります - プルリクエストでの差分確認やレビューがしやすくなります
Directory.Build.props
ディレクトリ配下のすべてのプロジェクトに適用される共通の MSBuild プロパティです。
<Project>
<PropertyGroup>
<Authors>Your Team</Authors>
<Company>Your Company</Company>
<Copyright>Copyright © 2020-$([System.DateTime]::Now.Year) Your Company</Copyright>
<Product>Your Product</Product>
<PackageProjectUrl>https://github.com/yourorg/yourrepo</PackageProjectUrl>
<RepositoryUrl>https://github.com/yourorg/yourrepo</RepositoryUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
</PropertyGroup>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AnalysisLevel>latest-all</AnalysisLevel>
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup>
<NetStandardLibVersion>netstandard2.0</NetStandardLibVersion>
<NetLibVersion>net8.0</NetLibVersion>
<NetTestVersion>net9.0</NetTestVersion>
</PropertyGroup>
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackSources>true</EmbedUntrackSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<DebugType>embedded</DebugType>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)logo.png" Pack="true" PackagePath="\" />
<None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<PropertyGroup>
<PackageIcon>logo.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<Using Include="System.Collections.Immutable" />
</ItemGroup>
</Project>
入れ子になった Directory.Build.props
内側のファイルは、外側のファイルを自動的にインポートしません。
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup>
</PropertyGroup>
</Project>
Directory.Build.targets
プロジェクトの評価が完了した後にインポートされます。次の用途に使用します。
- 共通のアナライザーパッケージ参照
- カスタムビルドターゲット
- プロジェクトの種類に基づく条件付きロジック
<Project>
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" PrivateAssets="all" />
</ItemGroup>
</Project>
Directory.Packages.props — パッケージの一元管理
CPM を使用すると、NuGet パッケージのバージョンをリポジトリのルートで一元管理できます。個々の .csproj ファイルでは、Version 属性を指定せずにパッケージを参照します。
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<PropertyGroup>
<AkkaVersion>1.5.35</AkkaVersion>
<AspireVersion>9.1.0</AspireVersion>
</PropertyGroup>
<ItemGroup Label="App Dependencies">
<PackageVersion Include="Akka" Version="$(AkkaVersion)" />
<PackageVersion Include="Akka.Cluster" Version="$(AkkaVersion)" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
</ItemGroup>
<ItemGroup Label="Test Dependencies">
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="FluentAssertions" Version="7.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="coverlet.collector" Version="8.0.0" />
</ItemGroup>
<ItemGroup Label="Build Dependencies">
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
</ItemGroup>
</Project>
パッケージの使用(バージョン指定不要)
<ItemGroup>
<PackageReference Include="Akka" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>
バージョンの上書き
<PackageReference Include="Newtonsoft.Json" VersionOverride="13.0.3" />
.editorconfig
一貫したコードスタイルを適用するため、リポジトリのルートに配置します。
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{csproj,props,targets,xml,json,yml,yaml}]
indent_size = 2
[*.cs]
csharp_style_namespace_declarations = file_scoped:warning
csharp_prefer_braces = true:warning
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_style_require_accessibility_modifiers = always:warning
csharp_style_prefer_pattern_matching = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
csharp_using_directive_placement = outside_namespace:warning
dotnet_sort_system_directives_first = true
dotnet_naming_rule.private_fields_should_be_camel_case.symbols = private_fields
dotnet_naming_rule.private_fields_should_be_camel_case.style = camel_case_underscore
dotnet_naming_rule.private_fields_should_be_camel_case.severity = warning
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
dotnet_naming_style.camel_case_underscore.required_prefix = _
dotnet_naming_style.camel_case_underscore.capitalization = camel_case
global.json — SDK バージョンの固定
{
"sdk": {
"version": "9.0.200",
"rollForward": "latestFeature"
}
}
ロールフォワードポリシー
| ポリシー | 動作 |
|---|---|
disable | 完全に一致するバージョンが必要 |
patch | 同じ major.minor の最新パッチバージョン |
feature | 同じ major の最新 minor.patch バージョン |
latestFeature | 同じ major の最新フィーチャーバンド |
minor | 同じ major の最新 minor バージョン |
latestMinor | 同じ major の最新 minor バージョン |
major | 最新の SDK(推奨されません) |
推奨: latestFeature — 同じフィーチャーバンド内でのパッチ更新を許可します。
nuget.config
パッケージソースとセキュリティを設定します。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
<clear />、明示的なソース、<packageSourceMapping> を組み合わせるパターンにより、サプライチェーン攻撃を防止できます。
プライベートフィードの場合:
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="internal" value="https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
<packageSource key="internal">
<package pattern="MyCompany.*" />
</packageSource>
</packageSourceMapping>
NuGet Audit
.NET 9+ では、NuGetAudit がデフォルトで有効になっています。
<PropertyGroup>
<NuGetAudit>true</NuGetAudit>
<NuGetAuditLevel>low</NuGetAuditLevel>
<NuGetAuditMode>all</NuGetAuditMode>
</PropertyGroup>
ロックファイル
再現性のある復元を有効にします。
<PropertyGroup>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
CI では:
dotnet restore --locked-mode
SourceLink と決定論的ビルド
NuGet に公開するライブラリの場合:
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackSources>true</EmbedUntrackSources>
<DebugType>embedded</DebugType>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="all" />
</ItemGroup>
RELEASE_NOTES.md によるバージョン管理
#### 1.2.0 January 15th 2025 ####
- Added new feature X
- Fixed bug in Y
#### 1.1.0 December 10th 2024 ####
- Initial release
CI/CD との統合
- name: Update version from release notes
shell: pwsh
run: ./build.ps1
- name: Build
run: dotnet build -c Release
- name: Pack with tag version
run: dotnet pack -c Release /p:PackageVersion=${{ github.ref_name }}
クイックリファレンス
| ファイル | 用途 |
|---|---|
MySolution.slnx | 最新の XML ソリューションファイル |
Directory.Build.props | ビルドプロパティの一元管理 |
Directory.Packages.props | パッケージバージョンの一元管理 |
global.json | SDK バージョンの固定 |
NuGet.Config | パッケージソースの設定 |
RELEASE_NOTES.md | バージョン履歴 |
.editorconfig | コードスタイルの適用 |
.config/dotnet-tools.json | ローカル .NET ツール |