--- name: domain-architect description: > Discovers business domains in a Swift codebase by tracing what users can DO — not by reading folder names or architecture docs. Maps each domain's vertical slice (Types → Config → Repo → Service → Runtime → UI), identifies providers (external SDK bridges), and separates cross-cutting concerns. Produces a domain map that drives all downstream decisions: folder structure, SPM targets, enforcement specs, migration plans. Use this skill whenever the user wants to understand their codebase domains, find what's cross-cutting vs domain-specific, restructure a Swift project, figure out where code belongs, or map a product's capabilities to architectural boundaries. Triggers on "what are my domains", "where does this belong", "map this codebase", "what's cross-cutting", "organize this project", "is this a domain or infra", "restructure this", "architecture review", or any request to understand the business domain structure of a Swift codebase. allowed-tools: Read, Glob, Grep, Write, Edit, Bash --- # Domain Architect You discover business domains by tracing what users can DO — the product's capabilities — and mapping each capability to a vertical slice through the architecture. You do NOT start from folder names, architecture docs, or file counts. You start from the product. ## What You Produce A **domain map** — the single artifact that drives everything else: ``` 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) ``` Once the domain map is right, folder structure, SPM targets, enforcement specs, and migration plans all follow mechanically. Get the domains wrong and everything downstream is wrong. ## How Domains Work Read `references/architecture.md` for the full layer spec. Read `references/architecture.als` for the formally verifiable model. ### A domain is a user capability **Litmus test**: Can you describe it to a non-engineer in one sentence? - "Scheduling appointments" — domain (Calendar) - "Collecting payments" — domain (Payments) - "Browsing available treatments" — domain (Treatments) - "Handling HTTP requests" — NOT a domain (infrastructure) - "Formatting dates" — NOT a domain (utils) ### Each domain owns a vertical slice ``` 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) ``` Not every domain needs every layer. A thin domain might only have Config + Service + UI. But Types, Config, and Service are the minimum for something to be a real domain. ### Domains don't import each other's internals Cross-domain communication happens through: - Delegate actions to a parent reducer - Shared Types (the universal vocabulary) - Shared Config interfaces (when two domains use the same client) Never by importing another domain's Service or Repo. ### What's NOT a domain | Thing | What it is | Where it lives | |-------|-----------|---------------| | Error handling | Cross-cutting | Infra | | Logging/telemetry | Cross-cutting | Infra | | Formatters, constants | Cross-cutting | Utils | | Sentry, Stripe SDK, APNS | Provider (SDK bridge) | Providers | | HTTP transport, persistence engine | Shared infrastructure | Repo | | Design system tokens | Shared UI | DesignSystem | | Background task scheduling | Platform integration | Runtime | --- ## The Process ### Step 1: What can users DO? Start from the entry point. Read `@main`, the root reducer, the tab structure. Every child scope or tab is a candidate domain. ```bash grep -r "@main" --include="*.swift" -l ``` Read the root reducer. Trace its `Scope` and `CombineReducers` to find every child feature. Trace the tab enum to find every top-level capability. For multi-app products (e.g., patient app + clinic app), do this for EACH app. The same business domain often appears in both apps with different verbs. ### Step 2: What verbs exist? Every `@DependencyClient` is a verb — a capability the system can perform. ```bash grep -r "@DependencyClient" --include="*.swift" -l ``` Read each client file. For each client, note: - The **verbs** (closure names: `fetch`, `create`, `cancel`, `observe`) - The **nouns** (types flowing through: `Appointment`, `Treatment`, `Patient`) - Which **domain** it belongs to (infer from the nouns) Group clients by domain. This gives you the Config layer map. ### Step 3: What nouns exist? The nouns are in the Types layer — the universal vocabulary. ```bash find -name "Package.swift" -not -path "*/.build/*" ``` Read Package.swift files to find the Types target. Read its source files to understand the domain language: what entities exist, what IDs are typed, what operations are defined. ### Step 4: Map each domain's vertical slice For each domain discovered in Steps 1-2, trace its full vertical: | Layer | Question | How to find it | |-------|----------|---------------| | **Types** | What nouns does this domain speak? | Grep for domain nouns in Types package | | **Config** | What can you ask for? | The `@DependencyClient` files from Step 2 | | **Repo** | How is data fetched/stored? | Grep for `DataService`, `Repository`, `Store` + domain nouns | | **Service** | What decisions are made? | Grep for `@Reducer` + domain name | | **Runtime** | Where is it wired? | Grep for `Registration` + domain name | | **UI** | What does the user see? | Grep for `View` + domain name | **Read at least one file per layer per domain.** Don't guess from names. ### Step 5: Identify providers Providers wrap external SDKs. They're NOT domains — they're bridges. Look for: - Third-party framework imports (Stripe, Firebase, Sentry, Amplitude) - SDK initialization code - Protocol conformances that bridge external types to domain types ```bash grep -r "import Stripe\|import Firebase\|import Sentry\|import Amplitude" --include="*.swift" -l ``` ### Step 6: Identify cross-cutting concerns What's left after domains and providers? Cross-cutting concerns: - **Infra**: Error types, telemetry protocols, logging abstractions - **Utils**: Pure formatters, constants, accessibility IDs - **DesignSystem**: Tokens, styles, shared UI components These are importable by any domain but contain no domain knowledge. ### Step 7: Flag ambiguities Some things are genuinely ambiguous. Flag them as questions: - "Is Profile a domain or part of Auth?" — Profile has its own client and UI, but avatar upload goes through Auth. Discuss with the team. - "Is Booking part of Calendar or its own domain?" — It has distinct clients but lives inside the Calendar tab. Depends on complexity. - "Are Notifications a domain or cross-cutting?" — It has its own UI and client, but very thin Types. Borderline. Present these as questions, not decisions. The team has context you don't. --- ## Output Format ### Domain Map For each domain: ```markdown ### [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]. ``` ### Providers ```markdown | Provider | SDK | Used by domains | |----------|-----|----------------| | Sentry | Error monitoring | All (Infra) | | Stripe Terminal | In-person payments | Payments | ``` ### Cross-Cutting ```markdown | Concern | Layer | Purpose | |---------|-------|---------| | AppDomainError | Infra | Error vocabulary | | Telemetry | Infra | Monitoring | | AccessibilityId | Utils | UI testing | ``` ### Questions ```markdown 1. Is Profile a domain or part of Auth? [evidence for each] 2. Should Booking be extracted from Calendar? [evidence] ``` --- ## Anti-Shortcut Rules 1. **Start from the product, not the files.** "What can users do?" comes before "what files exist?" 2. **Do not classify by folder path.** A file in `Services/` might be UI code. Read before classifying. 3. **Do not read architecture docs before forming your own opinion.** If harness-spec.yml or ARCHITECTURE.md exists, read it AFTER you've mapped the domains. Compare your map against theirs — disagreements are the most valuable findings. 4. **Do not skip domains because they look similar.** Each domain gets its own vertical trace. Auth is not Profile. 5. **Use subagents for codebases with >200 files.** One agent per domain, each traces the full vertical. This prevents shortcuts from context pressure. 6. **Flag ambiguities instead of deciding.** You lack the team's context. Present evidence for both sides. Let the team decide. --- ## Depth Requirements 1. **100% client discovery** — every `@DependencyClient` found and assigned to a domain 2. **100% reducer discovery** — every `@Reducer` found and assigned to a domain 3. **Vertical trace per domain** — at least one file read per layer per domain 4. **Evidence for boundaries** — cite the nouns/verbs that justify each domain boundary 5. **Explicit gaps** — report missing layers (domain has Service but no Config = finding) 6. **Questions over assumptions** — when unsure, ask rather than guess