一键导入
clean-architecture
クリーンアーキテクチャに基づく設計指針スキル。プロジェクト構成、レイヤー分離、依存関係の方向、テスタビリティ確保の原則を定義する。 新規プロジェクト作成、機能追加、リファクタリング、テスト作成時に使用すること。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
クリーンアーキテクチャに基づく設計指針スキル。プロジェクト構成、レイヤー分離、依存関係の方向、テスタビリティ確保の原則を定義する。 新規プロジェクト作成、機能追加、リファクタリング、テスト作成時に使用すること。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
.NET 10 file-based apps を使ったスクリプト実行スキル。Copilot がデータ処理・ファイル操作・簡易ツールなどの スクリプト的な処理を行う際に、C# の file-based app として実行する方法を定義する。 使い捨ての処理はファイルを作成せず stdin パイプで実行し、再利用可能な処理のみ .cs ファイルとして保存すること。
ASP.NET Core Blazor (.NET 10) の最新機能・ベストプラクティスに基づいてコンポーネントやページを生成・レビュー・リファクタリングするスキル。 Blazor コンポーネント、ページ、レイアウト、JS interop、フォームバリデーション、状態管理の実装時に使用すること。
C# 14 / .NET 10 の最新文法に基づいてコードを生成・レビュー・リファクタリングするスキル。 C# のコード生成、レビュー、リファクタリング、新規ファイル作成時に使用すること。
| name | clean-architecture |
| description | クリーンアーキテクチャに基づく設計指針スキル。プロジェクト構成、レイヤー分離、依存関係の方向、テスタビリティ確保の原則を定義する。 新規プロジェクト作成、機能追加、リファクタリング、テスト作成時に使用すること。 |
クリーンアーキテクチャに従い、依存関係は常に内側に向かうこと。テスタビリティを最優先に設計せよ。
Presentation (Blazor / API)
↓
Application (ユースケース / CQRS)
↓
Domain (エンティティ / 値オブジェクト / ドメインサービス)
Infrastructure (DB / 外部 API / ファイル)
↓
Application (インターフェース実装)
依存ルール: 内側のレイヤーは外側のレイヤーを知らない。
| レイヤー | 責務 | 依存先 |
|---|---|---|
| Domain | エンティティ、値オブジェクト、ドメインイベント、リポジトリインターフェース | なし (外部依存ゼロ) |
| Application | ユースケース、DTO、バリデーション、インターフェース定義 | Domain のみ |
| Infrastructure | DB アクセス、外部 API クライアント、ファイル I/O、インターフェース実装 | Application, Domain |
| Presentation | UI (Blazor)、API コントローラー、DI 構成 | Application, Domain |
<SolutionName>/
├── src/
│ ├── <SolutionName>.Domain/ # エンティティ、値オブジェクト、インターフェース
│ ├── <SolutionName>.Application/ # ユースケース、DTO、バリデーション
│ ├── <SolutionName>.Infrastructure/ # EF Core、外部サービス実装
│ └── <SolutionName>.Web/ # Blazor / API (Presentation)
├── tests/
│ ├── <SolutionName>.Domain.Tests/ # ドメインロジックの単体テスト
│ ├── <SolutionName>.Application.Tests/ # ユースケースの単体テスト
│ └── <SolutionName>.Infrastructure.Tests/ # 統合テスト
└── <SolutionName>.slnx
プロジェクト作成は dotnet CLI で行うこと:
dotnet new classlib -n <SolutionName>.Domain
dotnet new classlib -n <SolutionName>.Application
dotnet new classlib -n <SolutionName>.Infrastructure
dotnet new blazor -n <SolutionName>.Web
dotnet new mstest -n <SolutionName>.Domain.Tests
dotnet new mstest -n <SolutionName>.Application.Tests
dotnet new mstest -n <SolutionName>.Infrastructure.Tests
dotnet sln add <各プロジェクトパス>
new による直接生成は禁止。// Application 層: インターフェース定義
public interface IOrderRepository
{
Task<Order?> GetByIdAsync(OrderId id, CancellationToken cancellationToken = default);
Task AddAsync(Order order, CancellationToken cancellationToken = default);
}
// Infrastructure 層: 実装
public class OrderRepository(AppDbContext db) : IOrderRepository
{
public async Task<Order?> GetByIdAsync(OrderId id, CancellationToken cancellationToken = default)
=> await db.Orders.FindAsync([id], cancellationToken);
public async Task AddAsync(Order order, CancellationToken cancellationToken = default)
=> await db.Orders.AddAsync(order, cancellationToken);
}
record を使うこと。// 値オブジェクト
public record OrderId(Guid Value);
// エンティティ
public class Order
{
public OrderId Id { get; private set; }
public OrderStatus Status { get; private set; }
public void Cancel()
{
if (Status is OrderStatus.Shipped)
throw new DomainException("出荷済みの注文はキャンセルできません");
Status = OrderStatus.Cancelled;
}
}
CancellationToken を必ず受け取ること。public class CancelOrderUseCase(IOrderRepository orderRepository, IUnitOfWork unitOfWork)
{
public async Task ExecuteAsync(OrderId orderId, CancellationToken cancellationToken = default)
{
var order = await orderRepository.GetByIdAsync(orderId, cancellationToken)
?? throw new NotFoundException(nameof(Order), orderId);
order.Cancel();
await unitOfWork.SaveChangesAsync(cancellationToken);
}
}
DbContext は Infrastructure 層に配置すること。HttpClient + IHttpClientFactory を使うこと。AddInfrastructure(this IServiceCollection) を用意すること。Add* 拡張メソッドを呼ぶこと。MSTest を使用すること。 xUnit / NUnit は使わない。
dotnet new mstest -n <ProjectName>.Tests
dotnet add <ProjectName>.Tests package Moq
dotnet add <ProjectName>.Tests package FluentAssertions
<対象クラス名>Tests とすること。[TestClass]
public class CancelOrderUseCaseTests
{
private readonly Mock<IOrderRepository> _orderRepositoryMock = new();
private readonly Mock<IUnitOfWork> _unitOfWorkMock = new();
private CancelOrderUseCase _sut = null!;
[TestInitialize]
public void Setup()
{
_sut = new CancelOrderUseCase(
_orderRepositoryMock.Object,
_unitOfWorkMock.Object);
}
[TestMethod]
public async Task ExecuteAsync_注文が存在する場合_キャンセルされること()
{
// Arrange
var orderId = new OrderId(Guid.NewGuid());
var order = OrderFactory.CreatePending(orderId);
_orderRepositoryMock
.Setup(r => r.GetByIdAsync(orderId, It.IsAny<CancellationToken>()))
.ReturnsAsync(order);
// Act
await _sut.ExecuteAsync(orderId);
// Assert
order.Status.Should().Be(OrderStatus.Cancelled);
_unitOfWorkMock.Verify(u => u.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once);
}
[TestMethod]
public async Task ExecuteAsync_注文が存在しない場合_NotFoundExceptionをスローすること()
{
// Arrange
var orderId = new OrderId(Guid.NewGuid());
_orderRepositoryMock
.Setup(r => r.GetByIdAsync(orderId, It.IsAny<CancellationToken>()))
.ReturnsAsync((Order?)null);
// Act
var act = () => _sut.ExecuteAsync(orderId);
// Assert
await act.Should().ThrowAsync<NotFoundException>();
}
}
| 規約 | 内容 |
|---|---|
| 命名 | メソッド名_条件_期待結果 (日本語可) |
| 構造 | Arrange / Act / Assert (AAA パターン) |
| モック | Moq を使用。インターフェースをモック化 |
| アサーション | FluentAssertions を推奨 |
| テストデータ | ファクトリメソッド / Builder パターンで生成 |
| 属性 | [TestClass], [TestMethod], [TestInitialize], [DataTestMethod], [DataRow] |
| 非同期 | async Task を返すこと (async void 禁止) |
| レベル | 対象 | モック範囲 | テストプロジェクト |
|---|---|---|---|
| 単体テスト | Domain エンティティ、値オブジェクト | 不要 | *.Domain.Tests |
| 単体テスト | ユースケース | リポジトリ・外部サービス | *.Application.Tests |
| 統合テスト | Infrastructure 実装 | In-Memory DB 使用 | *.Infrastructure.Tests |
DateTime.Now / Guid.NewGuid() の直接使用を避け、抽象化すること。sealed クラスでもインターフェースを抽出してテスト可能にすること。// ❌ テスト困難
public class OrderService
{
public Order Create() => new() { Id = new OrderId(Guid.NewGuid()), CreatedAt = DateTime.UtcNow };
}
// ✅ テスト容易
public class OrderService(IDateTimeProvider dateTimeProvider, IGuidProvider guidProvider)
{
public Order Create() => new() { Id = new OrderId(guidProvider.NewGuid()), CreatedAt = dateTimeProvider.UtcNow };
}
new DbContext() 等の手動インスタンス生成 (DI を使うこと)async void テストメソッド