dotnet-core について
.NET 8 バックエンドサービスの設計・保守に向けたアーキテクチャ指針です。レイヤー構成、ドメインモデル、検証、可観測性、テストを扱い、CQRSやAOTを採用する判断材料も示します。
主な機能
- 01内向き依存のレイヤー構成
- 02ファクトリーによる不変条件の維持
- 03CQRSとパイプラインビヘイビアの指針
- 04機能別にまとめるMinimal APIs
- 05ヘルスチェックと構造化ログ
- 06統合テストとコンテナの指針
活用シーン
- 01.NET 8サービスの構成設計
- 02ドメイン層とインフラ層の境界確認
- 03検証と横断的関心事の標準化
- 04ヘルスチェックと統合テストの設計
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ alexander-danilenko/ cortex-ai-skills --skill dotnet-coreインストール後、Claude Code で/dotnet-coreを入力して読み込まれたか確認します。
詳細
.NET Core アーキテクチャ
.NET 8 バックエンドサービスのアーキテクチャ規約です。ここではサービスの 構造 を扱います。言語レベルの規約については、csharp スキルを参照してください。
レイヤー構成
依存関係は内側にのみ向けます。Domain はほかの何も知りません。データベースや SDK を参照できるのは Infrastructure レイヤーだけです。そのため、内側のレイヤーはどちらにも依存せずにテストできます。
Domain entities, value objects, domain events, interfaces → no dependencies
Application use cases, CQRS handlers, DTOs, validators → Domain
Infrastructure EF Core, external APIs, implementations of Domain interfaces → Domain + Application
Api endpoints, DI wiring, middleware → all of the above
この原則が守られていることを確認するテストは、Domain が BCL 以外のパッケージ参照なしでコンパイルできるかどうかです。コンパイルできれば、境界は保たれています。
エンティティは自身の不変条件を守ります。セッターは private にし、EF Core のマテリアライザー用に引数なしの private コンストラクターを用意し、検証を行う static ファクトリを使います。public セッターがあると、どのレイヤーからでもエンティティを無効な状態にできてしまいます。そうなると、「ドメインが X を保証する」というのは単なるコメントにすぎません。
public class Product
{
public string Name { get; private set; } = string.Empty;
public decimal Price { get; private set; }
private Product() { } // EF Core materialisation only
public static Product Create(string name, decimal price) =>
price <= 0
? throw new DomainException("Price must be greater than zero")
: new Product { Name = name, Price = price };
}
規約
- 読み取りと書き込みが実際に異なる場合は、MediatR による CQRS を採用します — モデル、整合性、スケーリングが異なる場合です。CRUD サービスでは形式的な手続きになるため、デフォルトではなく、ドメインが必要とする場合に採用してください。
- 横断的関心事はハンドラーではなく、パイプラインビヘイビアに置きます。 検証、ロギング、トランザクションをビヘイビアとして一度実装すれば、すべてのハンドラーに適用され、新しいハンドラーで適用し忘れることもありません。
- Minimal API は
MapGroupでグループ化し、機能領域ごとにファイルを分けます。 すべてのエンドポイントを単一のProgram.csに置くと、ルートが二十個ほどになった時点で読みづらくなります。 - 検証は Application 境界で行います(ビヘイビア内の FluentValidation)。これにより、どのエントリーポイントからユースケースが呼び出されても同じルールが適用されます。エンドポイントでの検証では、HTTP 経由の場合しか保護できません。
- DbContext は Infrastructure に置き、Application レイヤーが所有するインターフェイスの背後に隠します。 DbContext をハンドラーに漏らすと、ユースケースが EF に結び付き、プロバイダーなしではテストできなくなります。
- シークレットを
appsettings.jsonに入れてはいけません — ローカルではユーザーシークレット、本番環境では環境変数または Vault を使います。コミットされた情報は漏えいしたものと見なされ、ローテーションには再デプロイが必要になります。 - ヘルスチェック(
/health/live、/health/ready)と、相関 ID を使った構造化ログを最初から導入します。 インシデント発生中に可観測性を後付けしようとするのは、何もないことに気付くタイミングとして最悪です。二つのプローブは明確に異なるものにしてください。readiness ではデータベースや下流の API を確認してもかまいませんが、liveness ではプロセス自体が生存しているかだけを確認します。liveness プローブがデータベースに ping を送ると、短時間のデータベース障害ですべての Pod が再起動され、回復可能な依存先の障害が全面停止につながります。 - コンテナーは非 root ユーザーで実行します。
USER行を追加してください。デフォルトの root コンテナーでは、プロセスレベルの侵害がホストレベルにまで及びます。これを防ぐのに必要なのは 1 行だけです。 - 実際のデータベース(Testcontainers)を使い、
WebApplicationFactory経由で統合テストを行います。 DI グラフ、ルーティング、シリアル化、EF マッピングをテストできます。これらは、モックを使った単体テストではサービスが壊れていても成功と報告されるレイヤーです。 - 起動時間やフットプリントに見合う場合に限り、AOT を採用します。 AOT ではランタイムリフレクションが使えなくなり、一部のシリアライザーや DI のパターンが気付かないうちに壊れることがあります。採用を決める前に、スタック全体が AOT に対応できることを確認してください。
コメント
Markdown 対応