# Wpf Clean Architecture Mvvm

> Design guidelines for WPF applications using .NET 10 C#, MVVM Toolkit, Clean Architecture, Modular Monolith, and .NET Aspire. Use this skill when creating, modifying, or reviewing any part of this application's architecture, project structure, or code organization.

- Skill: `runceel/wpf-clean-architecture-mvvm` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add runceel/wpf-clean-architecture-mvvm`
- Raw SKILL.md: https://api.skillmd.com/api/skills/runceel/wpf-clean-architecture-mvvm/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: runceel (https://skillmd.com/u/runceel)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/runceel/wpf-clean-architecture-mvvm

---


# WPF Clean Architecture + Modular Monolith + Aspire Design Guidelines

**.NET 10 C# + WPF** で **MVVM Toolkit**・**クリーンアーキテクチャ**・**モジュラーモノリス**・**.NET Aspire** を組み合わせて構築します。

## 参照ドキュメント

| ドキュメント | 内容 |
|---|---|
| [shell-pattern.md](references/shell-pattern.md) | IModule・IShellService のインターフェース定義と全実装例 |
| [aspire-integration.md](references/aspire-integration.md) | Aspire 統合の詳細設定・コード例 |
| [project-settings.md](references/project-settings.md) | 各プロジェクトの `.csproj` 設定とプロジェクト参照の例 |

---

## ソリューション構成

```
src/
  AspireAppHost/          # Aspire オーケストレーター（バックエンドインフラ管理）
  ServiceDefaults/        # OpenTelemetry・サービスディスカバリの共通設定
  WpfApp/                 # WPF シェル（最小限のグルーコードのみ）
  Shared/
    Shared.Kernel/        # ドメインプリミティブ（WPF 非依存）
    Shared.UI/            # UI プリミティブ（IModule・IShellService・共通スタイル）
  Modules/
    {ModuleName}/
      {ModuleName}.Domain/
      {ModuleName}.Application/
      {ModuleName}.Infrastructure/
      {ModuleName}.Presentation/   # Views・ViewModels・IModule 実装
tests/
  {ModuleName}.{Layer}.Tests/
```

---

## モジュラーモノリスのルール

- **モジュール間の直接参照は禁止**。モジュール間通信は `Shared.Kernel` 経由（イベント・インターフェース・共通 DTO）のみ。
- **UI もモジュールが所有する**。Views・ViewModels・ナビゲーション登録はすべて `Presentation` 層が管理する。
- **`WpfApp` は薄いシェルのみ**。`IModule` の列挙・初期化とシェル UI（ウィンドウ枠）だけを持ち、ビジネス知識を持たない。
- 各モジュールは DI 登録拡張メソッドを `Infrastructure` 層（`AddXxxModule`）と `Presentation` 層（`AddXxxPresentation`）にそれぞれ実装する。

---

## クリーンアーキテクチャ 各層の責務

| 層 | 依存先 | 主な内容 |
|---|---|---|
| **Domain** | `Shared.Kernel` のみ | エンティティ・値オブジェクト・集約ルート・リポジトリ IF・ドメイン例外 |
| **Application** | Domain・Shared.Kernel | コマンド/クエリ・ハンドラー・DTO・バリデーション・ドメインイベントハンドラー |
| **Infrastructure** | Application・Domain・Shared.Kernel | リポジトリ実装・EF Core・外部クライアント・DI 登録 |
| **Presentation** | Application・Shared.Kernel・Shared.UI | Views (XAML)・ViewModels (MVVM Toolkit)・`IModule` 実装・DI 登録 |

---

## MVVM Toolkit 使用規則

- ViewModel は `ObservableObject` を継承した `partial class` で定義する。
- プロパティは `[ObservableProperty]`（フィールド名 `_camelCase`）、コマンドは `[RelayCommand]` で生成する。
- プロパティ変更時の副作用は `partial void OnXxxChanged(T value)` で対応する。
- `CanExecute` は `[RelayCommand(CanExecute = nameof(CanXxx))]` で指定する。
- ViewModel と View は Presentation 層の `AddXxxPresentation()` で DI 登録する。

```csharp
public partial class OrderListViewModel(IOrderQueryService queryService) : ObservableObject
{
    [ObservableProperty] private ObservableCollection<OrderDto> _orders = [];
    [ObservableProperty] private bool _isLoading;

    [RelayCommand]
    private async Task LoadOrdersAsync(CancellationToken ct)
    {
        IsLoading = true;
        try { Orders = new(await queryService.GetAllAsync(ct)); }
        finally { IsLoading = false; }
    }
}
```

---

## Shared.Kernel の内容（WPF 非依存）

`Entity<TId>`・`ValueObject`・`AggregateRoot<TId>`・`IDomainEvent`・`Result<T>`・共通例外基底・`IEventBus`

## Shared.UI の内容（WPF 依存）

`IModule`・`IShellService`・`NavigationItem`・共通 `ResourceDictionary`（テーマ・スタイル）・共通コンバーター基底

インターフェース定義と実装例 → [shell-pattern.md](references/shell-pattern.md)

---

## WpfApp（シェル）の責務

Generic Host (`Host.CreateApplicationBuilder()`) で起動し、`AddServiceDefaults()` を組み込む。

1. `ShellService` / `MainWindowViewModel` / `MainWindow` を DI 登録
2. 各モジュールの `AddXxxModule()` + `AddXxxPresentation()` を呼び出す
3. ビルド後、全 `IModule` を取得して `module.Initialize(shell)` を実行
4. `MainWindow` を表示する

完全なコード → [shell-pattern.md](references/shell-pattern.md)

---

## .NET Aspire 統合

- `AspireAppHost` はバックエンドインフラ（DB・API・キュー等）をオーケストレートする。WPF アプリは登録しない（手動起動）。
- `ServiceDefaults` を `WpfApp` から参照し、`AddServiceDefaults()` でテレメトリ・サービスディスカバリを有効化する。
- バックエンド API への接続は `https+http://{サービス名}` で行う（Aspire のサービスディスカバリ）。

詳細・コード例 → [aspire-integration.md](references/aspire-integration.md)

---

## 依存方向のルール（厳守）

```
Presentation  →  Application  →  Domain
Presentation  →  Shared.UI
Infrastructure  →  Application  →  Domain
WpfApp  →  {ModuleName}.Presentation（IModule 列挙のため）
WpfApp  →  Shared.UI
Shared.Kernel  ←  すべての層から参照可（UI 依存なし）
Shared.UI  ←  Presentation・WpfApp のみ参照可
AspireAppHost  →  バックエンドサービスプロジェクトのみ
モジュール間の直接参照  →  禁止
```

プロジェクトファイルと参照の設定例 → [project-settings.md](references/project-settings.md)

---

## コード生成時のチェックリスト

1. **新しいモジュール**: Domain → Application → Infrastructure → Presentation の順に4プロジェクトを作成し、Presentation から `Shared.UI` を参照する。
2. **ViewModel**: `ObservableObject` + `partial class` + `[ObservableProperty]`/`[RelayCommand]`。
3. **ナビゲーション画面の追加**: `IModule.Initialize()` に `NavigationItem` を登録し、View と ViewModel を DI 登録する。
4. **モジュール間通信**: `Shared.Kernel` の `IEventBus` や共通 DTO を経由する。モジュール直接参照は禁止。
5. **新しいユースケース**: Application 層にコマンド/クエリ＋ハンドラーを追加し、ドメインロジックは Domain 層に保持する。
6. **バックエンドサービス追加**: `AspireAppHost` に登録し、Infrastructure 層で `https+http://{サービス名}` でアクセスする。
7. **新規プロジェクト作成**: `ServiceDefaults` を参照し、ホストビルダーで `AddServiceDefaults()` を呼び出す。

