원클릭으로
clean-architecture-guide
Modular Monolith + Clean Architecture の設計ガイド。新しいモジュールの作成、画面の実装、機能追加を行う際には、必ずこのスキルを参照して従ってください。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Modular Monolith + Clean Architecture の設計ガイド。新しいモジュールの作成、画面の実装、機能追加を行う際には、必ずこのスキルを参照して従ってください。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
Provides up-to-date information about ASP.NET Core 10 and Blazor .NET 10 new features. Use when the user asks about Blazor .NET 10 capabilities, ASP.NET Core 10 features, OpenAPI, Minimal APIs, SignalR, or needs guidance on ASP.NET Core/Blazor .NET 10 specific features.
Design Azure infrastructure using natural language, or analyze existing Azure resources to auto-generate architecture diagrams, refine them through conversation, and deploy with Bicep. When to use this skill: - "Create X on Azure", "Set up a RAG architecture" (new design) - "Analyze my current Azure infrastructure", "Draw a diagram for rg-xxx" (existing analysis) - "Foundry is slow", "I want to reduce costs", "Strengthen security" (natural language modification) - Azure resource deployment, Bicep template generation, IaC code generation - Microsoft Foundry, AI Search, OpenAI, Fabric, ADLS Gen2, Databricks, and all Azure services
Create a new specification as a GitHub Issue, optimized for Generative AI consumption.
ドキュメント作成時の共通ルール。Markdown ドキュメントを新規作成・更新する際には、必ずこのスキルを参照して従ってください。
Ensure .NET/C# code meets best practices for the solution/project.
Provides up-to-date information about .NET 10 and C# 14 new features for C# developers. Use when the user asks about .NET 10 capabilities, C# 14 language features, ASP.NET Core 10 Blazor, EF Core 10, or needs guidance on .NET 10/C# 14 specific features.
SOC 직업 분류 기준
| name | clean-architecture-guide |
| description | Modular Monolith + Clean Architecture の設計ガイド。新しいモジュールの作成、画面の実装、機能追加を行う際には、必ずこのスキルを参照して従ってください。 |
このスキルは、本プロジェクトにおける Modular Monolith + Clean Architecture の具体的な設計パターンとルールを定義します。
重要: このスキルに基づいてコード編集・生成を行う際には、対応する言語・プラットフォームのスキル(例:
dotnet10、aspnetcore-blazor10、aspire等)を必ず併せて参照してください。最新の言語機能や API を正しく使用するために必要です。
本プロジェクトは .NET Aspire を使用してアプリケーションの構成・実行・監視を行います。
aspire run で全リソースを起動し、Aspire Dashboard でログ・トレース・メトリクスを確認しますaspire publish でマニフェストを生成し、Azure Container Apps 等にデプロイします.WithReference())を通じて自動的に解決されますsrc/
AppHost/ # Aspire AppHost(オーケストレーション)
ServiceDefaults/ # 共通のサービス設定(テレメトリ、ヘルスチェック等)
Web/ # Blazor Server フロントエンド
Modules/<ModuleName>/ # 各モジュール(Clean Architecture 3 層)
Aspire の詳細な API やパターンについては
aspireスキルを参照してください。
新しいモジュールは以下の 3 プロジェクトで構成します。
src/Modules/<ModuleName>/
<ModuleName>.Domain/ # エンティティ、値オブジェクト(依存なし)
<ModuleName>.Application/ # UseCase、DTO、リポジトリインターフェース(Domain のみ依存)
<ModuleName>.Infrastructure/ # リポジトリ実装、外部サービス連携、DI 登録(Application に依存)
# 1. プロジェクト作成
dotnet new classlib -n <ModuleName>.Domain -o src/Modules/<ModuleName>/<ModuleName>.Domain --framework net10.0
dotnet new classlib -n <ModuleName>.Application -o src/Modules/<ModuleName>/<ModuleName>.Application --framework net10.0
dotnet new classlib -n <ModuleName>.Infrastructure -o src/Modules/<ModuleName>/<ModuleName>.Infrastructure --framework net10.0
# 2. ソリューションに追加
dotnet sln add src/Modules/<ModuleName>/<ModuleName>.Domain
dotnet sln add src/Modules/<ModuleName>/<ModuleName>.Application
dotnet sln add src/Modules/<ModuleName>/<ModuleName>.Infrastructure
# 3. プロジェクト参照設定
dotnet add src/Modules/<ModuleName>/<ModuleName>.Application reference src/Modules/<ModuleName>/<ModuleName>.Domain
dotnet add src/Modules/<ModuleName>/<ModuleName>.Infrastructure reference src/Modules/<ModuleName>/<ModuleName>.Application
# 4. Infrastructure に DI パッケージ追加
dotnet add src/Modules/<ModuleName>/<ModuleName>.Infrastructure package Microsoft.Extensions.DependencyInjection.Abstractions
# 5. Infrastructure に Azure Cosmos DB SDK パッケージ追加
dotnet add src/Modules/<ModuleName>/<ModuleName>.Infrastructure package Microsoft.Azure.Cosmos
# 6. Web からの参照追加
dotnet add src/Web reference src/Modules/<ModuleName>/<ModuleName>.Application
dotnet add src/Web reference src/Modules/<ModuleName>/<ModuleName>.Infrastructure
# 7. 自動生成された Class1.cs を各プロジェクトから削除
Web ──→ Application (UseCase インターフェース、DTO のみ)
Web ──→ Infrastructure (DI 登録の拡張メソッド呼び出しのためのみ)
Infrastructure ──→ Application (リポジトリインターフェースの実装)
Application ──→ Domain (エンティティの利用)
Domain ──→ (依存なし)
| ルール | 説明 |
|---|---|
| Web から Domain エンティティを直接参照しない | Web 層は Application 層の DTO のみを使用する |
| Web から Repository を直接注入しない | Web 層は UseCase インターフェース経由でのみデータにアクセスする |
| ビジネスロジックを Web / Infrastructure に書かない | ビジネスロジックは Domain と Application のみに配置する |
| Application 層から Infrastructure を参照しない | Application 層はインターフェースを定義し、実装は Infrastructure 層が行う |
| リポジトリ内で個別の永続化呼び出しは不要 | Cosmos DB SDK の各操作(CreateItemAsync, ReplaceItemAsync 等)は即座に永続化される。EF Core のような SaveChangesAsync パターンは不要 |
ビジネスの中核となるデータ構造を定義します。
namespace <ModuleName>.Domain;
public class <EntityName>
{
public int ID { get; set; }
public string Name { get; set; } = "";
// ビジネスルールに関わるメソッドもここに配置可能
}
Web 層に公開するデータ転送オブジェクトです。Domain エンティティの代わりにこれを使います。
namespace <ModuleName>.Application;
public record <EntityName>Dto(int ID, string Name);
データアクセスの抽象を定義します。UseCase 内部から使用され、Web 層には公開しません。
namespace <ModuleName>.Application;
public interface I<EntityName>Repository
{
Task<IReadOnlyList<Domain.<EntityName>>> GetAllAsync();
Task UpdateAsync(int id, ...);
}
同一パーティション内で複数操作をアトミックに実行する必要がある場合のみ使用します。Cosmos DB SDK の各操作(CreateItemAsync 等)は個別に即座永続化されるため、単一操作の UseCase では不要です。
namespace <ModuleName>.Application;
/// <summary>
/// 同一パーティション内の複数操作をアトミックに実行する場合に使用。
/// Cosmos DB の TransactionalBatch に対応する。
/// 単一操作の UseCase では不要。
/// </summary>
public interface IUnitOfWork
{
Task ExecuteBatchAsync(CancellationToken cancellationToken = default);
}
ポイント: Cosmos DB では個別の CRUD 操作が即座に永続化されるため、EF Core のような
SaveChangesAsyncパターンは不要です。IUnitOfWorkは同一パーティション内の TransactionalBatch が必要な場合のみ使用します。
Web 層が唯一依存する操作インターフェース です。1 つの操作 = 1 つの UseCase とし、ExecuteAsync メソッドを持ちます。
namespace <ModuleName>.Application.UseCases;
// インターフェース
public interface IGet<EntityName>sUseCase
{
Task<IReadOnlyList<<EntityName>Dto>> ExecuteAsync(string? searchText = null);
}
// 実装(Application 層内に配置)
public class Get<EntityName>sUseCase(I<EntityName>Repository repository) : IGet<EntityName>sUseCase
{
public async Task<IReadOnlyList<<EntityName>Dto>> ExecuteAsync(string? searchText = null)
{
var entities = await repository.GetAllAsync();
// フィルタリングなどのビジネスロジックはここで行う
var filtered = string.IsNullOrEmpty(searchText)
? entities
: entities.Where(e => e.Name.Contains(searchText, StringComparison.OrdinalIgnoreCase)).ToList();
// Domain → DTO 変換
return filtered.Select(e => new <EntityName>Dto(e.ID, e.Name)).ToList();
}
}
namespace <ModuleName>.Application.UseCases;
public interface IUpdate<EntityName>UseCase
{
Task ExecuteAsync(int id, ...);
}
public class Update<EntityName>UseCase(I<EntityName>Repository repository) : IUpdate<EntityName>UseCase
{
public async Task ExecuteAsync(int id, ...)
{
await repository.UpdateAsync(id, ...);
// Cosmos DB SDK では各操作が即座に永続化されるため、SaveChangesAsync は不要
}
}
データアクセスには Azure Cosmos DB SDK(Microsoft.Azure.Cosmos) を直接使用します。モジュールが使用する Cosmos DB コンテナへのアクセスを提供するプロバイダークラスを定義します。
using Microsoft.Azure.Cosmos;
namespace <ModuleName>.Infrastructure;
/// <summary>
/// モジュールが使用する Cosmos DB コンテナへのアクセスを提供する。
/// </summary>
public class <ModuleName>CosmosContainers
{
public Container <ContainerName> { get; }
public <ModuleName>CosmosContainers(CosmosClient cosmosClient, string databaseName)
{
var database = cosmosClient.GetDatabase(databaseName);
<ContainerName> = database.GetContainer("<container-name>");
}
}
ポイント: Cosmos DB SDK の各操作(
CreateItemAsync,ReadItemAsync,ReplaceItemAsync,DeleteItemAsync)は即座に永続化されます。EF Core のような変更追跡やSaveChangesAsyncパターンは不要です。
リポジトリは <ModuleName>CosmosContainers を注入し、Cosmos DB SDK を使ってデータアクセスを行います。各操作は即座に永続化されます。
using <ModuleName>.Domain;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Cosmos.Linq;
namespace <ModuleName>.Infrastructure;
public class <EntityName>Repository(<ModuleName>CosmosContainers containers) : Application.I<EntityName>Repository
{
public async Task<IReadOnlyList<<EntityName>>> GetAllAsync()
{
var query = containers.<ContainerName>
.GetItemLinqQueryable<<EntityName>>()
.Where(e => e.Type == "<entity-type>")
.ToFeedIterator();
var results = new List<<EntityName>>();
while (query.HasMoreResults)
{
var response = await query.ReadNextAsync();
results.AddRange(response);
}
return results;
}
public async Task UpdateAsync(string id, string partitionKey, ...)
{
var response = await containers.<ContainerName>.ReadItemAsync<<EntityName>>(
id, new PartitionKey(partitionKey));
var item = response.Resource;
// プロパティの更新
await containers.<ContainerName>.ReplaceItemAsync(item, id, new PartitionKey(partitionKey));
// Cosmos DB では ReplaceItemAsync で即座に永続化される
}
}
モジュール単位で Add<ModuleName>Module() 拡張メソッドを提供します。
using <ModuleName>.Application;
using <ModuleName>.Application.UseCases;
using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.DependencyInjection;
namespace <ModuleName>.Infrastructure;
public static class <ModuleName>ServiceCollectionExtensions
{
public static IServiceCollection Add<ModuleName>Module(this IServiceCollection services, CosmosClient cosmosClient, string databaseName)
{
// Cosmos DB コンテナプロバイダー
services.AddSingleton(new <ModuleName>CosmosContainers(cosmosClient, databaseName));
// Infrastructure(リポジトリ実装)
services.AddScoped<I<EntityName>Repository, <EntityName>Repository>();
// Application(UseCase)
services.AddScoped<IGet<EntityName>sUseCase, Get<EntityName>sUseCase>();
services.AddScoped<IUpdate<EntityName>UseCase, Update<EntityName>UseCase>();
return services;
}
}
Web 層は UseCase インターフェースと DTO のみ を使用します。
@page "/<route>"
@using <ModuleName>.Application
@using <ModuleName>.Application.UseCases
@rendermode InteractiveServer
<PageTitle>ページタイトル</PageTitle>
<h1>ページタイトル</h1>
@* UI コンポーネント *@
@code {
[Inject]
private IGet<EntityName>sUseCase Get<EntityName>sUseCase { get; set; } = default!;
[Inject]
private IUpdate<EntityName>UseCase Update<EntityName>UseCase { get; set; } = default!;
private IReadOnlyList<<EntityName>Dto> items = [];
protected override async Task OnInitializedAsync()
{
items = await Get<EntityName>sUseCase.ExecuteAsync();
}
}
Web プロジェクトでは Aspire ServiceDefaults を追加し、各モジュールの DI 登録を行います。
接続文字列は Aspire AppHost の .WithReference() によって自動注入されます。
using <ModuleName>.Infrastructure;
var builder = WebApplication.CreateBuilder(args);
// Aspire ServiceDefaults(テレメトリ、ヘルスチェック等)
builder.AddServiceDefaults();
// モジュール登録(CosmosClient は Aspire 経由で注入、DB 名は設定から取得)
var cosmosClient = new CosmosClient(builder.Configuration.GetConnectionString("cosmos")!);
builder.Services.Add<ModuleName>Module(cosmosClient, "{projectname}-db");
var app = builder.Build();
// Aspire デフォルトエンドポイント(ヘルスチェック等)
app.MapDefaultEndpoints();
AppHost の Program.cs で Web プロジェクトとインフラリソースを接続します。
var builder = DistributedApplication.CreateBuilder(args);
// インフラリソース
var cosmos = builder.AddAzureCosmosDB("cosmos")
.AddDatabase("{projectname}-db");
var cache = builder.AddRedis("cache");
// Web アプリケーション
builder.AddProject<Projects.Web>("web")
.WithReference(cosmos)
.WithReference(cache)
.WaitFor(cosmos);
builder.Build().Run();
ポイント: モジュールが使用する Cosmos DB やキャッシュなどのインフラリソースは AppHost で定義し、
.WithReference()で Web プロジェクトに渡します。Web のProgram.csではbuilder.Configuration.GetConnectionString()で自動的にその接続情報を取得できます。Cosmos DB のデータベース名・コンテナ名は各モジュールの DI 登録で指定します。
新しいモジュールや機能を実装する際は、単体テストとインテグレーションテストの両方を必ず作成してください。
MSTest の詳細なベストプラクティスは
csharp-mstestスキルを参照してください。 Aspire Testing の詳細な API はaspireスキルの Testing リファレンス を参照してください。
tests/
<ModuleName>.Application.Tests/ # 単体テスト(UseCase、ドメインロジック)
AppHost.IntegrationTests/ # インテグレーションテスト(Aspire AppHost 経由)
UseCase とドメインロジックの単体テストを モジュールごとに 作成します。
# 1. テストプロジェクト作成
dotnet new mstest -n <ModuleName>.Application.Tests -o tests/<ModuleName>.Application.Tests --framework net10.0
# 2. ソリューションに追加
dotnet sln add tests/<ModuleName>.Application.Tests
# 3. テスト対象への参照追加
dotnet add tests/<ModuleName>.Application.Tests reference src/Modules/<ModuleName>/<ModuleName>.Application
I<EntityName>Repository と IUnitOfWork をモック/スタブで差し替えMethodName_Scenario_ExpectedBehaviorusing <ModuleName>.Application.UseCases;
using <ModuleName>.Domain;
namespace <ModuleName>.Application.Tests;
[TestClass]
public sealed class Get<EntityName>sUseCaseTests
{
[TestMethod]
public async Task ExecuteAsync_WithSearchText_ReturnsFilteredResults()
{
// Arrange
var repository = new Fake<EntityName>Repository([
new <EntityName> { ID = 1, Name = "Alpha" },
new <EntityName> { ID = 2, Name = "Beta" },
]);
var useCase = new Get<EntityName>sUseCase(repository);
// Act
var result = await useCase.ExecuteAsync("Alpha");
// Assert
Assert.AreEqual(1, result.Count);
Assert.AreEqual("Alpha", result[0].Name);
}
}
Aspire Hosting.Testing を使い、AppHost から全リソースを起動して End-to-End に近いテストを行います。
# 1. テストプロジェクト作成
dotnet new mstest -n AppHost.IntegrationTests -o tests/AppHost.IntegrationTests --framework net10.0
# 2. ソリューションに追加
dotnet sln add tests/AppHost.IntegrationTests
# 3. AppHost への参照と Aspire Testing パッケージ追加
dotnet add tests/AppHost.IntegrationTests reference src/AppHost
dotnet add tests/AppHost.IntegrationTests package Aspire.Hosting.Testing
IsAspireTestProject を追加<PropertyGroup>
<IsAspireTestProject>true</IsAspireTestProject>
</PropertyGroup>
DistributedApplicationTestingBuilder で AppHost を丸ごと起動await using で確実にクリーンアップusing System.Net;
using Aspire.Hosting.Testing;
namespace AppHost.IntegrationTests;
[TestClass]
public sealed class HealthCheckTests
{
[TestMethod]
[Timeout(120_000)]
public async Task Web_HealthEndpoint_ReturnsOk()
{
// Arrange
var builder = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AppHost>();
await using var app = await builder.BuildAsync();
await app.StartAsync();
await app.WaitForResourceReadyAsync("web");
// Act
var client = app.CreateHttpClient("web");
var response = await client.GetAsync("/health");
// Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
}
| ルール | 説明 |
|---|---|
| 単体テストはモジュール単位 | 各モジュールの Application.Tests プロジェクトに配置 |
| インテグレーションテストは AppHost 単位 | AppHost.IntegrationTests に集約 |
| テストは独立 | テスト間で状態を共有しない。各テストが自己完結すること |
| リポジトリをモックする | 単体テストでは DB に依存しない。Fake/Mock/Stub を使用 |
| 実インフラで検証する | インテグレーションテストでは Aspire が起動する実コンテナ(DB、キャッシュ等)を使用 |
| PR 前に全テスト通過 | dotnet test {ProjectName}.slnx が成功することを確認してから PR を作成(github-flow 参照) |
新しいモジュールや機能を実装する際に確認してください。
Add<ModuleName>Module() を追加したか.WithReference() で Web に接続しているかbuilder.AddServiceDefaults() と app.MapDefaultEndpoints() を追加しているかdotnet build が成功するかaspire run で正常に起動し、Aspire Dashboard でサービスが確認できるか<ModuleName>.Application.Tests)を作成したかAppHost.IntegrationTests)にヘルスチェック等のテストを追加したかdotnet test {ProjectName}.slnx が全件成功するか