| name | plan-v2-extract-domain |
| description | Collaboratively identify and formalize an existing codebase concept as a named domain. No files are moved — this is about communication and traceability, not refactoring. |
Please deep think / ultrathink as this is a complex task.
extract-domain
Collaboratively identify and formalize an existing codebase concept as a named domain without moving files. This is a workshop with the user, not an automated refactor. The goal is communication — future plans can reference "the auth domain" and everyone knows what that means.
User input:
$ARGUMENTS
# Expected:
# <concept> — the concept to extract (e.g., "authentication", "billing")
# Optional flags:
# --domain <slug> — override the auto-generated slug
# --list — list existing domains and exit
What This Command Does
- Explores the codebase to discover all code related to the concept
- Catalogues discovered files and their roles — presented to user for review
- Workshops with user to define what belongs vs. doesn't (boundary)
- Identifies public interfaces other code already consumes (contracts)
- Maps internal components (services, adapters, repos — composition)
- Writes
docs/domains/<slug>/domain.md pointing to files where they currently live
- Updates
docs/domains/registry.md (creates it if first domain)
What This Command Does NOT Do
- ❌ Move files to a new directory structure
- ❌ Refactor imports
- ❌ Create new source directories
- ❌ Change any code
Step 0: List Mode
If --list flag provided:
- Read
docs/domains/registry.md (if exists)
- Display all registered domains with slug, type, status
- If
docs/domains/domain-map.md exists, show it too — the map gives the visual overview of how domains relate
- If no registry exists, report "No domains registered yet"
- Exit
Step 1: Check for Existing Domain
- Read
docs/domains/registry.md if it exists
- Read
docs/domains/domain-map.md if it exists — understand the current domain topology, existing contract relationships, and where a new domain might fit
- Check if a domain with matching slug already exists
- If exists: report it and ask user whether to update or abort
- Generate slug from concept: lowercase, replace spaces with hyphens, strip non-alphanumeric
Step 2: Explore
Launch 2 research subagents in parallel to discover code related to the concept:
Subagent 1: Code Discovery
"Find all code related to the concept '' in this codebase.
Search for:
- Services, handlers, controllers related to
- Adapters wrapping external SDKs/APIs for
- Repositories/data access for
- Models, types, interfaces for
- Configuration files for
- Middleware, hooks, plugins for
- Tests related to
Output: List of files with suspected roles (service, adapter, repository, model, config, test, middleware, other)"
Subagent 2: Dependency & Contract Discovery
"Find how other parts of the codebase consume '' functionality.
Search for:
- Imports/requires from -related files
- Public interfaces/classes that other modules depend on
- Event channels, message queues related to
- Shared types/contracts consumed externally
Output: List of public interfaces with their consumers"
Step 3: Present & Workshop
Present discovered files to the user in a table:
I found these files related to "<concept>":
| # | File | Suspected Role | Include? |
|---|------|---------------|----------|
| 1 | src/services/auth-service.ts | Service | ✅ |
| 2 | src/middleware/jwt.ts | Middleware | ? |
| 3 | src/models/user.ts | Model (shared?) | ? |
| ... | ... | ... | ... |
Ask the user to confirm/adjust using the ask_user tool:
- For uncertain files: "Should
<file> be part of the <concept> domain, or does it belong elsewhere?"
- Boundary definition: "What does this domain OWN vs. what does it NOT own?"
- Shared files: "Some files serve multiple concepts. Should I note them as shared?"
- Contracts: "These interfaces are consumed by other code — are they the right public surface?"
Key principle: The agent proposes, the user decides. Domain boundary identification is a collaborative conversation.
Step 3.5: Identify Concepts
Group discovered contracts (from Subagent 2) into named concepts — consumer-facing capabilities that the domain offers. Each concept groups related contracts into one capability a consumer might search for.
For each concept, identify:
- Concept name: A verb phrase describing what consumers can do (e.g., "Subscribe to file changes", "Authenticate a user")
- Entry point: The primary contract/function a consumer imports first
- What it does: One sentence describing the capability
Grouping rules:
- Related contracts that serve one consumer use case = one concept
- A single contract can be its own concept if it's independently useful
- All concepts that a consumer might search for get a row — not limited to a "top 3"
Present the proposed concepts to the user for confirmation:
Based on the contracts discovered, I'd group them into these concepts:
| Concept | Entry Point | What It Does |
|---------|-------------|-------------|
| [verb phrase] | [primary contract] | [one line] |
Ask: "Do these concepts cover everything this domain offers? Should any be split, merged, or renamed?"
Step 4: Write Domain Files
4a: Create domain.md
Create docs/domains/<slug>/domain.md (mkdir -p the directory):
# Domain: [Name]
**Slug**: [kebab-case]
**Type**: business | infrastructure
**Created**: [ISO-8601]
**Created By**: extracted from existing codebase
**Status**: active
## Purpose
[1-3 sentences from user discussion: What business concept does this domain own?
Why does it exist? What would break if it didn't?]
## Concepts
[What this domain offers to consumers. Scannable table + narrative per concept.
Built from Step 3.5 — the confirmed concept groupings.]
| Concept | Entry Point | What It Does |
|---------|-------------|-------------|
| [verb phrase from Step 3.5] | [primary contract/function] | [one line description] |
### [Concept Name]
[2-3 sentences: When would a consumer use this? What's the pattern?]
```[language]
import { EntryPoint } from '@domains/[slug]';
// 3-5 line usage example showing the happy path
```
[Repeat for each concept identified in Step 3.5]
## Boundary
### Owns
- [Concept 1 — from user confirmation]
- [Concept 2]
### Does NOT Own
- [Boundary clarification — note which domain owns it if known]
## Contracts (Public Interface)
[Identified from Subagent 2 — what other code consumes]
| Contract | Type | Consumers | Description |
|----------|------|-----------|-------------|
## Composition (Internal)
[Mapped from Subagent 1 — the lego blocks]
| Component | Role | Depends On |
|-----------|------|------------|
## Source Location
[Files WHERE THEY CURRENTLY LIVE — no reorganization]
Primary: `[most common path prefix, or "scattered"]`
| File | Role | Notes |
|------|------|-------|
## Dependencies
### This Domain Depends On
- [Other domains or libraries this domain uses]
### Domains That Depend On This
- [From contract consumer analysis]
## History
| Plan | What Changed | Date |
|------|-------------|------|
| *(extracted)* | Domain extracted from existing codebase | [today] |
Required sections (plan-7 validates these exist):
- Purpose
- Concepts (⚠️ Review if missing when contracts exist)
- Boundary (Owns + Does NOT Own)
- Contracts
- Composition
- Source Location
- Dependencies
- History
Additional files are welcome in the domain folder — sketches, research notes, diagrams, API explorations. The domain.md is locked down; everything else is freeform.
4b: Create or Update Registry
If docs/domains/registry.md does not exist, create it:
# Domain Registry
| Domain | Slug | Type | Parent | Created By | Status |
|--------|------|------|--------|------------|--------|
## Domain Types
- **business**: User-facing business capability
- **infrastructure**: Cross-cutting technical capability serving other domains
## Domain Statuses
- **active**: In use and accepting changes
- **deprecated**: Being phased out (note successor domain)
- **archived**: Code remains but is no longer modified
Add a row for the new domain:
| [Name] | [slug] | business | — | extracted | active |
For infrastructure domains, use _platform as parent:
| [Name] | _platform/[slug] | infrastructure | _platform | extracted | active |
Step 5: Update Domain Map
Update docs/domains/domain-map.md (create if missing). This is a living Mermaid diagram showing all domains as components with their contract relationships.
If docs/domains/domain-map.md does not exist, create it:
# Domain Map
> Auto-maintained by plan commands. Shows all domains and their contract relationships.
> Domains are first-class components — this diagram is the system architecture at business level.
```mermaid
flowchart LR
classDef business fill:#E3F2FD,stroke:#2196F3,color:#000
classDef infra fill:#F3E5F5,stroke:#9C27B0,color:#000
classDef new fill:#FFF3E0,stroke:#FF9800,color:#000
%% Domains as components
%% Add domains here as they are discovered/created
Legend
- Blue: Business domains (user-facing capabilities)
- Purple: Infrastructure domains (cross-cutting technical capabilities)
- Orange: Newly added domains (change to blue/purple after first implementation)
- Solid arrows (→): Contract dependency (A consumes B's contract)
- Labels on arrows: Contract name being consumed
Then add the new domain to the diagram. Each domain is a node, and contract dependencies are labeled edges:
```mermaid
flowchart LR
classDef business fill:#E3F2FD,stroke:#2196F3,color:#000
classDef infra fill:#F3E5F5,stroke:#9C27B0,color:#000
%% Business domains
auth["🔐 auth\nIAuthService\nAuthEvents"]:::business
billing["💰 billing\nIBillingService"]:::business
notifications["📧 notifications\nINotificationService"]:::business
%% Infrastructure domains
_platform["⚙️ _platform\nILogger · IConfig"]:::infra
_platform_data["💾 _platform/data-access\nDatabaseConnection"]:::infra
%% Contract dependencies (A consumes B)
auth -->|DatabaseConnection| _platform_data
auth -->|ILogger| _platform
billing -->|IAuthService| auth
billing -->|ILogger| _platform
notifications -->|AuthEvents| auth
notifications -->|ILogger| _platform
Diagram rules:
- Each domain node shows: emoji + slug + key contract names it EXPOSES (one per line)
- Edges show which contract is being consumed, labeled with the contract name
- Business domains use
:::business (blue), infrastructure uses :::infra (purple)
- New domains being added in this session use
:::new (orange) temporarily
_platform children show as separate nodes with full slug
- Keep it readable — if a domain has 5+ contracts, show top 2-3 and add "..."
- Direction is LR (left-to-right) — infrastructure on the right, business on the left
When updating an existing map:
- Read the current diagram from
docs/domains/domain-map.md
- Add the new domain node with its exposed contracts
- Add edges for dependencies (from domain.md § Dependencies)
- If existing domains now depend on the new domain, add those edges too
- Update the Domain Health Summary table:
## Domain Health Summary
| Domain | Contracts Out | Consumers | Contracts In | Providers | Status |
|--------|--------------|-----------|-------------|-----------|--------|
For each domain on the map, fill in:
- Contracts Out: What it exposes (from node label)
- Consumers: Which domains have edges pointing TO this domain
- Contracts In: What it consumes (from outgoing edges)
- Providers: Which domains it has edges pointing FROM
- Status: ✅ Healthy (has contracts, edges labeled, no circular business deps), ⚠️ Review (zero consumers for mature domain, or high fan-in), ❌ Problem (circular business dep, unlabeled edges, no contracts)
Step 6: Report
✅ Domain extracted: <slug>
- Files catalogued: N
- Contracts identified: N
- Location: docs/domains/<slug>/domain.md
- Registry: docs/domains/registry.md (updated)
- Map: docs/domains/domain-map.md (updated)
Note: No files were moved. Source Location in domain.md
points to files in their current locations. A future plan
can consolidate files into src/<slug>/ if desired.
Infrastructure Domains
For cross-cutting concepts (logging, config, data access):
- Use
_platform as the parent domain
- Slug format:
_platform/data-access, _platform/observability
- Source location:
src/_platform/data-access/ (or wherever files currently live)
- Type:
infrastructure
Start coarse — _platform alone is fine. Split into child domains when a concern grows large enough.
Dependency Rules
business domain ──→ infrastructure domain ✅ (always allowed)
infrastructure ──→ business domain ❌ (never allowed)
business domain ──→ business domain ⚠️ (only via contracts)
Domain Granularity
A domain should represent a business conversation — concepts that change together for the same business reasons.
- Too granular:
login/, registration/, token-refresh/ → merge into auth/
- Right size:
auth/ — all authentication & authorization
- Too coarse:
backend/ — everything server-side
Split signal: Different plans consistently touch only one half of the domain.
Merge signal: Every plan that touches one also touches the other.
Brownfield Adoption
Domains are built incrementally and collaboratively. No big-bang migration.
- Only formalize domains as plans naturally encounter them
- Existing code that isn't part of an active plan doesn't move
- A project might have 2 domains formalized and 80% of code still in legacy locations — that's fine
- Coverage grows over time as plans touch more areas