clean architecture は、システムの中心にあるビジネスロジックを外部環境(UIやデータベースなど)から分離し、依存関係を内向きに向けることで、変更への耐性と保守性を高める設計手法です。本稿では、ASP.NET Core 6.0 を用いて実際のプロジェクト構成とコード実装を通じて、その具体的な設計と実現方法を解説します。
カテゴリ別プロジェクト構成
クリーンアーキテクチャでは、依存関係の方向性为核心(Core)に集約されるよう設計します。これにより、ビジネスルールの変更に対して影響範囲を最小限に抑え、テスト容易性や再利用性を担保します。実装は主に以下の3つのフォルダブロックで構成されます。
Core 層(ビジネスロジック中心)
このレイヤーは、ドメイン知識・業務ルールを表すコードのみを含み、他の層への依存を一切持ちません。
-
Iot.Domain ドメインエンティティ、値オブジェクト、 búsquedaキーワード基底インターフェース(例:
IMaterialRepository)、列挙型、定数などを定義するプロジェクトです。リポジトリインターフェースはこの層で定義し、実装は下層(Infrastructure)で行います。 -
Iot.Application ビジネスフローを司るサービス・DTOs・コマンド・クエリ(CQRS)・イベントハンドラ・例外クラス・AutoMapperProfile などを配置します。MediatR を活用してリクエスト・レスポンス パイプラインを統一的に管理します。 依存先:
Iot.Domain
# Core 層プロジェクト作成例
cd src
mkdir 1.core && cd 1.core
dotnet new classlib -n Iot.Domain --framework net6.0
dotnet new classlib -n Iot.Application --framework net6.0
cd Iot.Application
dotnet add reference ../Iot.Domain/Iot.Domain.csproj
Infrastructure 層(外部インフラ実装)
抽象レイヤーで定義されたコンポーネントの concrete 実装を提供します。これには永続化処理、メール/SMS 送信、外部 Web API 連携なども含まれます。
-
Iot.Data Entity Framework Core によるデータベースアクセス、マイグレーション設定、農業機器デバイス等のリポジトリ実装を担います。依存は上記
Iot.ApplicationおよびIot.Domain。 -
Iot.Shared 複数サービス間で共有したいヘルパークラスや構成(例:
EmailConfig,ShipmentDateConverter,RetryPolicy)などまとめます。他のサービスに NuGet パッケージとして配布可能な形態も想定。
# Infrastructure 層
cd ../../
mkdir 2.infrastructure && cd 2.infrastructure
dotnet new classlib -n Iot.Data --framework net6.0
dotnet new classlib -n Iot.Shared --framework net6.0
cd Iot.Data
dotnet add reference ../../1.core/Iot.Domain/Iot.Domain.csproj
dotnet add reference ../../1.core/Iot.Application/Iot.Application.csproj
cd ../Iot.Shared
dotnet add reference ../../1.core/Iot.Application/Iot.Application.csproj
Presentation 層(エントリーポイント)
HTTP リクエストを処理し、Application 層のサービスをオーケストレーションします。API コントローラー、フィルター、JWT 認証 설정、OpenAPI サポートなどを含みます。
- Iot.WebApi
Program.cs,Startup.cs(必要時), API コントローラー群(例:MaterialsController)を配置。依存はIot.ApplicationとIot.Data(DI 注入用に)。
また、フロント側(Vue.js 3 アプリ)と通信するための正当なAPIベースを提供し、CORS やレスポンス整形などの要件に対応します。
# Presentation 層 - WebAPI プロジェクト
cd ../../
mkdir 3.presentation && cd 3.presentation
dotnet new webapi -n Iot.WebApi --framework net6.0
cd Iot.WebApi
dotnet add reference ../../1.core/Iot.Application/Iot.Application.csproj
dotnet add reference ../../2.infrastructure/Iot.Data/Iot.Data.csproj
dotnet add reference ../../2.infrastructure/Iot.Shared/Iot.Shared.csproj
解決策全容の登録と依存管理
すべてのプロジェクトが作成されたら、Unified Solution として一つにまとめます。
cd ../../
dotnet new sln -n IoTPlatform
dotnet sln add ./src/1.core/Iot.Domain/Iot.Domain.csproj
dotnet sln add ./src/1.core/Iot.Application/Iot.Application.csproj
dotnet sln add ./src/2.infrastructure/Iot.Data/Iot.Data.csproj
dotnet sln add ./src/2.infrastructure/Iot.Shared/Iot.Shared.csproj
dotnet sln add ./src/3.presentation/Iot.WebApi/Iot.WebApi.csproj
依存のチェックや build 時のエラー監視には以下のコマンドが有用です:
dotnet build src/1.core/Iot.Application/Iot.Application.csproj
dotnet build src/2.infrastructure/Iot.Data/Iot.Data.csproj
dotnet build src/3.presentation/Iot.WebApi/Iot.WebApi.csproj
これにより、別のポリシーで独立テスト可能な状態を維持しつつ、CI/CD パイプラインへの統合が容易になります。
テスト戦略の設計
クリーンアーキテクチャでは、テストの独立性とカバレッジを重視します。
-
単体テスト(Unit Test) アプリケーション层面のロジックのみを対象にし、外部依存はモック(Moq、NSubstitute)や InMemory DB を使って代替します。XUnit/NUnit/MSTest いずれもサポート可。
-
統合テスト(Integration Test) DI コンテナを介した実環境に近いライフサイクルを再現。
WebApplicationFactoryを活用し、HTTP レベルでの振る舞いを検証。データベースはrequested単位でトランザクション Rollback を活用し、テスト結果の整合性を保ちます。 -
E2E / 機能テスト Playwright や Selenium を使った UI 自動検証も可能。ただし、クリーンアーキテクチャ下では、UIテストは必要最低限に抑え、ロジックテスト主導(strategy-first)のアプローチを推奨します。
// テストプロジェクト例(Iot.Application.Test)
public class MaterialServiceTests
{
[Fact]
public async Task CreateMaterial_ShouldReturnCreatedResult_WhenValidInput()
{
// Arrange
var mockRepo = new Mock<IMaterialRepository>();
mockRepo.Setup(r => r.AddAsync(It.IsAny<Material>())).ReturnsAsync(new Material { Id = Guid.NewGuid() });
var service = new MaterialService(mockRepo.Object);
var dto = new CreateMaterialDto { Name = "TemperatureSensor", Unit = "℃" };
// Act
var result = await service.CreateAsync(dto);
// Assert
Assert.NotNull(result);
Assert.Equal("TemperatureSensor", result.Name);
mockRepo.Verify(r => r.AddAsync(It.IsAny<Material>()), Times.Once);
}
}
依存関係の強制遵守とレイヤー分離の利点
この構成によって得られる主な恩恵は、以下のような点です。
-
可視性 × 可逆性 ビジネスルールが単一の場所(Core)に集約されるため、変更要因を明確にbounds化できます。
-
テスト容易性 外部依存(DB, API, メールサーバー)がモック可能なことにより、CI 環境でも迅速にテスト実施が可能になります。
-
ポート・アダプタ形式の柔軟性 Presentation 層で Web/API/Swagger/Vue.js のいずれを採用しても、Application/Domain 層への変更は最小に抑えられます。
-
**将来的なマイクロサービス移行ス"" Shared レイヤーに独立させる対象(例: 個人認証モデル、住所正規化処理)を適切に抽出しておくことで、将来的なDDDに基づくサービス境界分割がスムーズになります。
このように、クリーンアーキテクチャは単なる「レイヤー分割」ではなく、エンタープライズアプリケーションの長期的な「進化可能性(Evolutionability)」を支える設計哲学そのものです。今後は、 Persistence Layer の実装や CQS パイプラインの構築、MediatR を使ったコントローラー統合までを解説していきます。