domain-architect について
domain-architectは、ユーザー向けの製品機能からビジネスドメインとアーキテクチャの垂直スライスを整理するスキルです。開発者がTypes、クライアント、Repo、Service、Runtime、UIを追跡し、レイヤーの不足や境界に関する疑問を記録できます。
主な機能
- 01機能起点のドメイン発見
- 02垂直スライスのレイヤー整理
- 03プロバイダーと横断的関心事の分類
- 04レイヤー不足と境界の疑問を記録
活用シーン
- 01既存のSwiftコードベースのドメイン整理
- 02依存クライアントとReducerの追跡
- 03チームでのドメイン境界の検討
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ pproenca/ dot-skills --skill domain-architectインストール後、Claude Code で/domain-architectを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
metadata.jsonreferences/architecture.alsarchitecture.mdprinciples.md詳細
ドメインアーキテクト
ユーザーが何を実行できるか、つまり製品の機能をたどり、各機能をアーキテクチャの垂直スライスに対応づけて、ビジネスドメインを見つけます。
フォルダー名、アーキテクチャドキュメント、ファイル数から始めるのではありません。製品から始めます。
作成するもの
すべての基盤となる、ドメインマップという単一の成果物です。
Domain Map
├── Business Domains (vertical slices the user would recognize)
│ └── Per domain: Types, Config clients, Service reducers, UI views
├── Providers (external SDK bridges)
├── Cross-Cutting Concerns (Infra, Utils)
└── Questions (ambiguous boundaries to discuss with the team)
ドメインマップが正しければ、フォルダー構成、SPM ターゲット、適用ルールの仕様、移行計画はすべて機械的に決まります。ドメインを誤ると、その後のすべてが誤ったものになります。
ドメインの仕組み
レイヤーの詳細な仕様は references/architecture.md をお読みください。
形式検証可能なモデルは references/architecture.als をお読みください。
ドメインとはユーザーの機能です
判定基準:エンジニア以外の人に、一文で説明できますか?
- 「予約をスケジュールする」— ドメイン(Calendar)
- 「支払いを受け付ける」— ドメイン(Payments)
- 「利用可能な施術を閲覧する」— ドメイン(Treatments)
- 「HTTP リクエストを処理する」— ドメインではありません(インフラストラクチャ)
- 「日付をフォーマットする」— ドメインではありません(ユーティリティ)
各ドメインは垂直スライスを所有します
Types → pure data definitions for this domain's nouns
Config → @DependencyClient interfaces (what you can ask for)
Repo → implementations (how it's done — API, persistence, sync)
Service → @Reducer state machines (business decisions)
Runtime → dependency wiring (Config interfaces → Repo implementations)
UI → SwiftUI views (pixels)
すべてのドメインにすべてのレイヤーが必要なわけではありません。小規模なドメインは、Config + Service + UI のみで構成される場合もあります。ただし、Types、Config、Service は、実際のドメインとして成立するために最低限必要です。
ドメイン同士で内部実装をインポートしません
ドメイン間の通信は、次の方法で行います。
- デリゲートアクションを親リデューサーに渡す
- Shared Types(共通の語彙)
- Shared Config インターフェース(2 つのドメインが同じクライアントを使う場合)
別のドメインの Service や Repo をインポートして通信することはありません。
ドメインではないもの
| 対象 | 内容 | 配置先 |
|---|---|---|
| エラー処理 | 横断的関心事 | Infra |
| ログ記録/テレメトリー | 横断的関心事 | Infra |
| フォーマッター、定数 | 横断的関心事 | Utils |
| Sentry、Stripe SDK、APNS | プロバイダー(SDK ブリッジ) | Providers |
| HTTP トランスポート、永続化エンジン | 共通インフラストラクチャ | Repo |
| デザインシステムのトークン | 共通 UI | DesignSystem |
| バックグラウンドタスクのスケジューリング | プラットフォーム連携 | Runtime |
プロセス
ステップ 1:ユーザーは何を実行できますか?
エントリーポイントから始めます。@main、ルートリデューサー、タブ構成を読みます。すべての子スコープやタブが、ドメイン候補です。
grep -r "@main" <project-root> --include="*.swift" -l
ルートリデューサーを読みます。Scope と CombineReducers をたどり、すべての子機能を見つけます。タブの列挙型をたどり、トップレベルの機能をすべて見つけます。
複数のアプリで構成される製品(例:患者向けアプリ + クリニック向けアプリ)では、各アプリについてこの作業を行います。同じビジネスドメインが、アプリごとに異なる動詞を使って登場することがよくあります。
ステップ 2:どのような動詞がありますか?
すべての @DependencyClient は動詞、つまりシステムが実行できる機能です。
grep -r "@DependencyClient" <project-root> --include="*.swift" -l
各クライアントファイルを読みます。各クライアントについて、次の点を記録します。
- 動詞(クロージャ名:
fetch、create、cancel、observe) - 名詞(受け渡される型:
Appointment、Treatment、Patient) - 所属するドメイン(名詞から推測します)
クライアントをドメインごとにグループ化します。これにより、Config レイヤーのマップが作成できます。
ステップ 3:どのような名詞がありますか?
名詞は Types レイヤーにあります。Types は共通の語彙です。
find <project-root> -name "Package.swift" -not -path "*/.build/*"
Package.swift ファイルを読み、Types ターゲットを見つけます。そのソースファイルを読み、ドメインの言語を理解します。どのようなエンティティが存在するか、どの ID に型が付いているか、どのような操作が定義されているかを確認します。
ステップ 4:各ドメインの垂直スライスをマッピングする
ステップ 1~2 で見つかった各ドメインについて、垂直構造全体をたどります。
| レイヤー | 確認すること | 調べ方 |
|---|---|---|
| Types | このドメインではどのような名詞が使われていますか? | Types パッケージでドメインの名詞を検索します |
| Config | どのような要求ができますか? | ステップ 2 の @DependencyClient ファイルを確認します |
| Repo | データはどのように取得・保存されていますか? | DataService、Repository、Store とドメインの名詞を検索します |
| Service | どのような判断が行われていますか? | @Reducer とドメイン名を検索します |
| Runtime | どこで接続されていますか? | Registration とドメイン名を検索します |
| UI | ユーザーには何が表示されますか? | View とドメイン名を検索します |
各ドメインの各レイヤーについて、少なくとも 1 つのファイルを読みます。 名前から推測しないでください。
ステップ 5:プロバイダーを特定する
プロバイダーは外部 SDK をラップします。ドメインではなく、ブリッジです。
以下を探します。
- サードパーティ製フレームワークのインポート(Stripe、Firebase、Sentry、Amplitude)
- SDK の初期化コード
- 外部の型をドメインの型に橋渡しするプロトコル準拠
grep -r "import Stripe\|import Firebase\|import Sentry\|import Amplitude" <project-root> --include="*.swift" -l
ステップ 6:横断的関心事を特定する
ドメインとプロバイダー以外に残るものは何でしょうか?それが横断的関心事です。
- Infra:エラー型、テレメトリープロトコル、ロギングの抽象化
- Utils:純粋なフォーマッター、定数、アクセシビリティ ID
- DesignSystem:トークン、スタイル、共有 UI コンポーネント
これらはどのドメインからもインポートできますが、ドメインの知識は含みません。
ステップ 7:曖昧な点を明示する
本当に曖昧なものもあります。質問として明示してください。
- 「Profile はドメインですか、それとも Auth の一部ですか?」— Profile には独自のクライアントと UI がありますが、アバターのアップロードは Auth を経由します。チームと相談してください。
- 「Booking は Calendar の一部ですか、それとも独立したドメインですか?」— 独自のクライアントがありますが、Calendar タブ内にあります。複雑さによって異なります。
- 「Notifications はドメインですか、それとも横断的関心事ですか?」— 独自の UI とクライアントがありますが、Types はごくわずかです。判断が分かれるところです。
決定事項ではなく、質問として提示してください。チームにはあなたの知らない背景情報があります。
出力形式
ドメインマップ
各ドメインについて:
### [Domain Name] — "[one-sentence description]"
**Nouns**: [Types this domain speaks — Appointment, Treatment, etc.]
**Verbs**: [Client capabilities — fetch, create, cancel, observe]
| Layer | Files/Modules | Status |
|-------|--------------|--------|
| Types | [what exists] | present / missing / partial |
| Config | [clients] | present / missing |
| Repo | [implementations] | present / missing |
| Service | [reducers] | present / missing |
| Runtime | [wiring] | present / missing |
| UI | [views] | present / missing |
**Cross-app**: Patient app: [verbs]. Clinic app: [verbs].
プロバイダー
| Provider | SDK | Used by domains |
|----------|-----|----------------|
| Sentry | Error monitoring | All (Infra) |
| Stripe Terminal | In-person payments | Payments |
横断的関心事
| Concern | Layer | Purpose |
|---------|-------|---------|
| AppDomainError | Infra | Error vocabulary |
| Telemetry | Infra | Monitoring |
| AccessibilityId | Utils | UI testing |
質問
1. Is Profile a domain or part of Auth? [evidence for each]
2. Should Booking be extracted from Calendar? [evidence]
近道を避けるルール
-
ファイルではなく、プロダクトから始めます。「ユーザーには何ができますか?」を「どのようなファイルがありますか?」より先に考えます。
-
フォルダーパスで分類しないでください。
Services/内のファイルが UI コードである場合もあります。分類する前に読みましょう。 -
自分なりの見解をまとめる前に、アーキテクチャのドキュメントを読まないでください。 harness-spec.yml や ARCHITECTURE.md がある場合は、ドメインのマッピングを終えてから読みます。自分のマップと照らし合わせてください。不一致こそ、最も価値のある発見です。
-
似ているように見えるからといって、ドメインを省略しないでください。 各ドメインについて、それぞれ独自に垂直構造をたどります。Auth は Profile ではありません。
-
200 ファイルを超えるコードベースでは、サブエージェントを使います。 ドメインごとにエージェントを 1 つ割り当て、それぞれが垂直構造全体をたどります。これにより、コンテキストの制約による近道を防げます。
-
判断するのではなく、曖昧な点を明示します。 チームの背景情報をあなたは持っていません。両方の見解を裏付ける根拠を提示し、チームに判断を委ねてください。
深度の要件
- クライアントの 100% を把握する — 見つかったすべての
@DependencyClientをドメインに割り当てます - リデューサーの 100% を把握する — 見つかったすべての
@Reducerをドメインに割り当てます - ドメインごとに垂直構造をたどる — 各ドメインの各レイヤーについて、少なくとも 1 つのファイルを読みます
- 境界の根拠を示す — 各ドメイン境界を正当化する名詞・動詞を挙げます
- 不足を明示する — 欠けているレイヤーを報告します(Service はあるが Config がないドメインなども、発見事項です)
- 推測より質問を優先する — 確信が持てない場合は推測せず、質問します