Stack Architecture Design
Design transport-agnostic handler systems with proper Result types and error taxonomy.
Process
Step 1: Understand Requirements
Gather information about:
- Transport surfaces — CLI, MCP, HTTP, or all?
- Domain operations — What actions does the system perform?
- Failure modes — What can go wrong? (maps to error taxonomy)
- External dependencies — APIs, databases, file system?
Step 2: Design Handler Layer
For each domain operation:
- Define input type (Zod schema)
- Define output type
- Identify possible error types (from taxonomy)
- Write handler signature:
Handler<Input, Output, Error1 | Error2>
Example:
// Input schema
const CreateUserInputSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
});
// Output type
interface User {
id: string;
email: string;
name: string;
}
// Handler signature
const createUser: Handler<unknown, User, ValidationError | ConflictError>;
Step 3: Map Errors to Taxonomy
Map domain errors to the 10 categories:
| Domain Error |
Stack Category |
Error Class |
| Not found |
not_found |
NotFoundError |
| Invalid input |
validation |
ValidationError |
| Already exists |
conflict |
ConflictError |
| No permission |
permission |
PermissionError |
| Auth required |
auth |
AuthError |
| Timed out |
timeout |
TimeoutError |
| Connection failed |
network |
NetworkError |
| Limit exceeded |
rate_limit |
RateLimitError |
| Bug/unexpected |
internal |
InternalError |
| User cancelled |
cancelled |
CancelledError |
Step 4: Choose Packages
Packages are organized into three tiers:
Package Tiers
┌─────────────────────────────────────────────────────────────────┐
│ TOOLING TIER │
│ Build-time, dev-time, test-time packages │
│ @outfitter/testing │
└─────────────────────────────────────────────────────────────────┘
▲
│ depends on
┌─────────────────────────────────────────────────────────────────┐
│ RUNTIME TIER │
│ Application-specific packages for different deployment targets │
│ @outfitter/cli @outfitter/mcp @outfitter/daemon │
│ @outfitter/config @outfitter/logging @outfitter/file-ops │
│ @outfitter/state │
└─────────────────────────────────────────────────────────────────┘
▲
│ depends on
┌─────────────────────────────────────────────────────────────────┐
│ FOUNDATION TIER │
│ Zero-runtime-dependency core packages │
│ @outfitter/contracts @outfitter/types │
└─────────────────────────────────────────────────────────────────┘
| Tier |
Packages |
Dependency Rule |
| Foundation |
contracts, types |
No @outfitter/* deps |
| Runtime |
cli, mcp, daemon, config, logging, file-ops, state |
May depend on Foundation |
| Tooling |
testing |
May depend on Foundation + Runtime |
Package Selection
| Package |
Purpose |
When to Use |
@outfitter/contracts |
Result types, errors, Handler contract |
Always (foundation) |
@outfitter/types |
Type utilities, collection helpers |
Type manipulation |
@outfitter/cli |
CLI commands, output modes, formatting |
CLI applications |
@outfitter/mcp |
MCP server, tool registration |
AI agent tools |
@outfitter/config |
XDG paths, config loading |
Configuration needed |
@outfitter/logging |
Structured logging, redaction |
Logging needed |
@outfitter/daemon |
Background services, IPC |
Long-running services |
@outfitter/file-ops |
Secure paths, atomic writes, locking |
File operations |
@outfitter/state |
Pagination, cursor state |
Paginated data |
@outfitter/testing |
Test harnesses, fixtures |
Testing |
Selection criteria:
- All projects need
@outfitter/contracts (foundation)
- CLI applications add
@outfitter/cli (includes UI components)
- MCP servers add
@outfitter/mcp
- File operations need both
@outfitter/config (paths) and @outfitter/file-ops (safety)
Step 5: Design Context Flow
Determine:
- Entry points — Where is context created? (CLI main, MCP server, HTTP handler)
- Context contents — Logger, config, signal, workspaceRoot
- Tracing — How requestId flows through operations
Output Templates
Architecture Overview
Project: {PROJECT_NAME}
Transport Surfaces: {CLI | MCP | HTTP | ...}
Directory Structure:
├── src/
│ ├── handlers/ # Transport-agnostic business logic
│ │ ├── {handler-1}.ts
│ │ └── {handler-2}.ts
│ ├── commands/ # CLI adapter (if CLI)
│ ├── tools/ # MCP adapter (if MCP)
│ └── index.ts # Entry point
└── tests/
└── handlers/ # Handler tests
Dependencies:
├── @outfitter/contracts # Foundation (always)
├── @outfitter/{package-2} # {reason}
└── @outfitter/{package-3} # {reason}
Handler Inventory
| Handler |
Input |
Output |
Errors |
Description |
getUser |
GetUserInput |
User |
NotFoundError |
Fetch user by ID |
createUser |
CreateUserInput |
User |
ValidationError, ConflictError |
Create new user |
deleteUser |
DeleteUserInput |
void |
NotFoundError, PermissionError |
Remove user |
Error Strategy
Domain Errors → Stack Taxonomy:
{domain-error-1} → {stack-category} ({ErrorClass})
- When: {condition}
- Exit code: {code}
{domain-error-2} → {stack-category} ({ErrorClass})
- When: {condition}
- Exit code: {code}
Implementation Order
- Foundation — Install packages, create types
- Core handlers — Implement business logic with tests
- Transport adapters — Wire up CLI/MCP/HTTP
- Testing — Integration tests across transports
Constraints
Always:
- Recommend Result types over exceptions
- Map domain errors to taxonomy categories
- Design handlers as pure functions (input, context) → Result
- Consider all transport surfaces upfront
- Include error types in handler signatures
Never:
- Suggest throwing exceptions
- Design transport-specific logic in handlers
- Recommend hardcoded paths
- Skip error type planning
- Couple handlers to specific transports
Related Skills
outfitter-stack:stack-patterns — Reference for all patterns
outfitter:tdd — TDD implementation methodology
outfitter-stack:stack-templates — Templates for components
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: stack-architecture3description: Design stack-based systems using @outfitter/* packages. Use when planning new projects, choosing packages, designing handler architecture, or when "architecture", "design", "structure", "plan handlers", or "error taxonomy" are mentioned. Use when this capability is needed.4---56# Stack Architecture Design78Design transport-agnostic handler systems with proper Result types and error taxonomy.910## Process1112### Step 1: Understand Requirements1314Gather information about:1516- **Transport surfaces** — CLI, MCP, HTTP, or all?17- **Domain operations** — What actions does the system perform?18- **Failure modes** — What can go wrong? (maps to error taxonomy)19- **External dependencies** — APIs, databases, file system?2021### Step 2: Design Handler Layer2223For each domain operation:24251. Define input type (Zod schema)262. Define output type273. Identify possible error types (from taxonomy)284. Write handler signature: `Handler<Input, Output, Error1 | Error2>`2930**Example:**3132```typescript33// Input schema34const CreateUserInputSchema = z.object({35 email: z.string().email(),36 name: z.string().min(1),37});3839// Output type40interface User {41 id: string;42 email: string;43 name: string;44}4546// Handler signature47const createUser: Handler<unknown, User, ValidationError | ConflictError>;48```4950### Step 3: Map Errors to Taxonomy5152Map domain errors to the 10 categories:5354| Domain Error | Stack Category | Error Class |55|--------------|----------------|-------------|56| Not found | `not_found` | `NotFoundError` |57| Invalid input | `validation` | `ValidationError` |58| Already exists | `conflict` | `ConflictError` |59| No permission | `permission` | `PermissionError` |60| Auth required | `auth` | `AuthError` |61| Timed out | `timeout` | `TimeoutError` |62| Connection failed | `network` | `NetworkError` |63| Limit exceeded | `rate_limit` | `RateLimitError` |64| Bug/unexpected | `internal` | `InternalError` |65| User cancelled | `cancelled` | `CancelledError` |6667### Step 4: Choose Packages6869Packages are organized into three tiers:7071#### Package Tiers7273```74┌─────────────────────────────────────────────────────────────────┐75│ TOOLING TIER │76│ Build-time, dev-time, test-time packages │77│ @outfitter/testing │78└─────────────────────────────────────────────────────────────────┘79 ▲80 │ depends on81┌─────────────────────────────────────────────────────────────────┐82│ RUNTIME TIER │83│ Application-specific packages for different deployment targets │84│ @outfitter/cli @outfitter/mcp @outfitter/daemon │85│ @outfitter/config @outfitter/logging @outfitter/file-ops │86│ @outfitter/state │87└─────────────────────────────────────────────────────────────────┘88 ▲89 │ depends on90┌─────────────────────────────────────────────────────────────────┐91│ FOUNDATION TIER │92│ Zero-runtime-dependency core packages │93│ @outfitter/contracts @outfitter/types │94└─────────────────────────────────────────────────────────────────┘95```9697| Tier | Packages | Dependency Rule |98|------|----------|-----------------|99| **Foundation** | `contracts`, `types` | No @outfitter/* deps |100| **Runtime** | `cli`, `mcp`, `daemon`, `config`, `logging`, `file-ops`, `state` | May depend on Foundation |101| **Tooling** | `testing` | May depend on Foundation + Runtime |102103#### Package Selection104105| Package | Purpose | When to Use |106|---------|---------|-------------|107| `@outfitter/contracts` | Result types, errors, Handler contract | Always (foundation) |108| `@outfitter/types` | Type utilities, collection helpers | Type manipulation |109| `@outfitter/cli` | CLI commands, output modes, formatting | CLI applications |110| `@outfitter/mcp` | MCP server, tool registration | AI agent tools |111| `@outfitter/config` | XDG paths, config loading | Configuration needed |112| `@outfitter/logging` | Structured logging, redaction | Logging needed |113| `@outfitter/daemon` | Background services, IPC | Long-running services |114| `@outfitter/file-ops` | Secure paths, atomic writes, locking | File operations |115| `@outfitter/state` | Pagination, cursor state | Paginated data |116| `@outfitter/testing` | Test harnesses, fixtures | Testing |117118**Selection criteria:**119120- All projects need `@outfitter/contracts` (foundation)121- CLI applications add `@outfitter/cli` (includes UI components)122- MCP servers add `@outfitter/mcp`123- File operations need both `@outfitter/config` (paths) and `@outfitter/file-ops` (safety)124125### Step 5: Design Context Flow126127Determine:128129- **Entry points** — Where is context created? (CLI main, MCP server, HTTP handler)130- **Context contents** — Logger, config, signal, workspaceRoot131- **Tracing** — How requestId flows through operations132133## Output Templates134135### Architecture Overview136137```138Project: {PROJECT_NAME}139Transport Surfaces: {CLI | MCP | HTTP | ...}140141Directory Structure:142├── src/143│ ├── handlers/ # Transport-agnostic business logic144│ │ ├── {handler-1}.ts145│ │ └── {handler-2}.ts146│ ├── commands/ # CLI adapter (if CLI)147│ ├── tools/ # MCP adapter (if MCP)148│ └── index.ts # Entry point149└── tests/150 └── handlers/ # Handler tests151152Dependencies:153├── @outfitter/contracts # Foundation (always)154├── @outfitter/{package-2} # {reason}155└── @outfitter/{package-3} # {reason}156```157158### Handler Inventory159160| Handler | Input | Output | Errors | Description |161|---------|-------|--------|--------|-------------|162| `getUser` | `GetUserInput` | `User` | `NotFoundError` | Fetch user by ID |163| `createUser` | `CreateUserInput` | `User` | `ValidationError`, `ConflictError` | Create new user |164| `deleteUser` | `DeleteUserInput` | `void` | `NotFoundError`, `PermissionError` | Remove user |165166### Error Strategy167168```169Domain Errors → Stack Taxonomy:170171{domain-error-1} → {stack-category} ({ErrorClass})172 - When: {condition}173 - Exit code: {code}174175{domain-error-2} → {stack-category} ({ErrorClass})176 - When: {condition}177 - Exit code: {code}178```179180### Implementation Order1811821. **Foundation** — Install packages, create types1832. **Core handlers** — Implement business logic with tests1843. **Transport adapters** — Wire up CLI/MCP/HTTP1854. **Testing** — Integration tests across transports186187## Constraints188189**Always:**190- Recommend Result types over exceptions191- Map domain errors to taxonomy categories192- Design handlers as pure functions (input, context) → Result193- Consider all transport surfaces upfront194- Include error types in handler signatures195196**Never:**197- Suggest throwing exceptions198- Design transport-specific logic in handlers199- Recommend hardcoded paths200- Skip error type planning201- Couple handlers to specific transports202203## Related Skills204205- `outfitter-stack:stack-patterns` — Reference for all patterns206- `outfitter:tdd` — TDD implementation methodology207- `outfitter-stack:stack-templates` — Templates for components208209---210> Converted and distributed by [TomeVault](https://tomevault.io/claim/outfitter-dev) — claim your Tome and manage your conversions.211<!-- tomevault:4.0:skill_md:2026-04-11 -->