syntaxis/shared/templates/documentation/architecture.md.template
Jesús Pérez 9cef9b8d57 refactor: consolidate configuration directories
Merge _configs/ into config/ for single configuration directory.
Update all path references.

Changes:
- Move _configs/* to config/
- Update .gitignore for new patterns
- No code references to _configs/ found

Impact: -1 root directory (layout_conventions.md compliance)
2025-12-26 18:36:23 +00:00

595 lines
13 KiB
Plaintext

# {{TOOL_NAME}} Architecture
Complete system design and component documentation.
## High-Level Overview
{{TOOL_NAME}} is a production-grade tool implementing a **3-tier architecture**:
```
┌─────────────────────────────────────────────┐
│ Tier 3: CLI / User Interface │
│ crates/{{tool_name_kebab}}-cli/ │
│ - clap command parsing │
│ - Configuration discovery │
│ - User-friendly output │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Tier 2: REST API (optional) │
│ crates/{{tool_name_kebab}}-api/ │
│ - axum HTTP server │
│ - JSON request/response │
│ - RESTful endpoints │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Tier 1: Core Library │
│ crates/{{tool_name_kebab}}-core/ │
│ - Business logic │
│ - Domain types │
│ - Service layer │
│ - Error handling │
│ - Data persistence │
└─────────────────────────────────────────────┘
```
## Tier 1: Core Library
**Location**: `crates/{{tool_name_kebab}}-core/`
**Purpose**: Reusable, interface-agnostic business logic
### Components
#### Error Handling
**File**: `src/error.rs`
Canonical error struct pattern:
```rust
pub struct {{ToolName}}Error {
pub kind: {{ToolName}}ErrorKind,
pub context: String,
pub source: Option<Box<dyn Error>>,
pub backtrace: Backtrace,
}
```
**Error Types** (customize for your domain):
- `ConfigError` - Configuration issues
- `ValidationError` - Input validation
- `IoError` - File I/O failures
- `DatabaseError` - Database operations
- Custom domain errors as needed
**Usage**:
```rust
fn operation() -> Result<T> {
if condition {
return Err({{ToolName}}Error::new(
{{ToolName}}ErrorKind::ValidationError("message".into()),
"context about what failed",
));
}
Ok(value)
}
```
#### Domain Types
**File**: `src/types.rs`
Primary domain type and related structures:
```rust
pub struct {{MainType}} {
pub id: String, // UUID v4
pub name: String, // Human-readable name
pub description: String, // Details
pub metadata: serde_json::Value, // Extensibility
pub created_at: DateTime<Utc>, // Creation timestamp
pub updated_at: DateTime<Utc>, // Last modification
}
```
**Additional Types**:
- Enums for status, priority, categories
- Configuration structures (serde + validation)
- Query filters and responses
- Transfer objects for APIs
#### Service Layer
**File**: `src/handlers/service.rs`
Business logic implementation:
```rust
pub struct {{MainType}}Service {
items: Arc<Mutex<HashMap<String, {{MainType}}>>>,
}
impl {{MainType}}Service {
pub async fn create(...) -> Result<{{MainType}}>
pub async fn get(&self, id: &str) -> Result<{{MainType}}>
pub async fn list(&self) -> Result<Vec<{{MainType}}>>
pub async fn update(...) -> Result<{{MainType}}>
pub async fn delete(&self, id: &str) -> Result<()>
}
```
**Storage Options**:
- **In-memory** (template default) - Dev/testing only
- **SQLite** - Single-user, embedded
- **PostgreSQL** - Production, concurrent access
- **Custom** - Implement `Storage` trait
**Pattern**: Async/await with tokio for all I/O operations
#### Module Organization
```
src/
├── lib.rs # Exports and documentation
├── error.rs # {{ToolName}}Error, {{ToolName}}ErrorKind
├── types.rs # {{MainType}} and related types
└── handlers/
├── mod.rs # Module organization
└── service.rs # {{MainType}}Service, business logic
```
### Testing
**Unit Tests** (in each module file):
```rust
#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_create() { ... }
}
```
**Integration Tests** (`tests/` directory):
```
tests/
├── common/
│ └── mod.rs # Shared test utilities
└── integration_test.rs
```
**Requirements**:
- Minimum 15 tests per module
- Test success paths
- Test error paths
- Test edge cases
- All tests must pass before commit
## Tier 2: REST API (Optional)
**Location**: `crates/{{tool_name_kebab}}-api/`
**Purpose**: HTTP interface to core library
### Components
#### Web Server
**Framework**: axum (Rust async web framework)
```rust
#[tokio::main]
async fn main() -> Result<()> {
let app = Router::new()
.route("/health", get(health_check))
.route("/{{main_type_plural}}", get(list).post(create))
.route("/{{main_type_plural}}/:id", get(get_one).put(update).delete(delete))
.with_state(Arc::new(state));
axum::serve(listener, app).await?;
Ok(())
}
```
#### Request/Response Types
**Request** (input from client):
```rust
#[derive(Deserialize)]
struct CreateRequest {
name: String,
description: String,
}
```
**Response** (output to client):
```rust
#[derive(Serialize)]
struct {{MainType}}Response {
id: String,
name: String,
created_at: DateTime<Utc>,
}
```
#### Endpoints
Standard CRUD with HTTP semantics:
| Method | Path | Description | Status |
|--------|------|-------------|--------|
| GET | `/health` | Health check | 200 OK |
| GET | `/{{main_type_plural}}` | List all | 200 OK |
| GET | `/{{main_type_plural}}/:id` | Get one | 200 OK or 404 |
| POST | `/{{main_type_plural}}` | Create | 201 Created |
| PUT | `/{{main_type_plural}}/:id` | Update | 200 OK or 404 |
| DELETE | `/{{main_type_plural}}/:id` | Delete | 204 No Content or 404 |
#### Error Handling
Structured error responses:
```json
{
"error": "Validation error",
"details": "Name is required",
"code": "VALIDATION_ERROR"
}
```
### Deployment
**Local Development**:
```bash
cargo run -p {{tool_name_kebab}}-api
# Server on http://127.0.0.1:3000
```
**Production**:
```bash
cargo build -p {{tool_name_kebab}}-api --release
./target/release/{{tool_name_kebab}}-api
```
**Docker**:
```dockerfile
FROM rust:latest as builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
COPY --from=builder /app/target/release/{{tool_name_kebab}}-api /usr/bin/
ENTRYPOINT ["{{tool_name_kebab}}-api"]
```
## Tier 3: CLI
**Location**: `crates/{{tool_name_kebab}}-cli/`
**Purpose**: User-friendly command-line interface
### Components
#### Command Parser
**Framework**: clap (derive macros for CLI parsing)
```rust
#[derive(Parser)]
#[command(name = "{{tool_name_kebab}}")]
#[command(about = "{{TOOL_NAME}} - {{SHORT_DESCRIPTION}}")]
struct Cli {
#[arg(short, long)]
config: Option<PathBuf>,
#[arg(short, long, global = true)]
verbose: bool,
#[command(subcommand)]
command: Commands,
}
```
#### Configuration Discovery
**Implementation**: tools-shared library
```rust
use tools_shared::find_config_path;
let config_path = find_config_path("{{tool_name_kebab}}.toml")
.unwrap_or_else(|| PathBuf::from(".project/{{tool_name_kebab}}.toml"));
```
**Search Order**:
1. `.project/{{tool_name_kebab}}.toml` - Tools ecosystem
2. `.vapora/{{tool_name_kebab}}.toml` - VAPORA projects
3. `.coder/{{tool_name_kebab}}.toml` - Doc tracking
4. `./{{tool_name_kebab}}.toml` - Current directory
**Help Text**:
```
CONFIGURATION SEARCH:
Looks for config in order (uses first found):
1. .project/{{tool_name_kebab}}.toml
2. .vapora/{{tool_name_kebab}}.toml
3. .coder/{{tool_name_kebab}}.toml
4. ./{{tool_name_kebab}}.toml
```
#### Commands
Organized into subcommands:
```rust
#[derive(Subcommand)]
enum Commands {
/// List items
List {
#[arg(short, long)]
filter: Option<String>,
#[arg(short, long, default_value = "20")]
limit: usize,
},
/// Show summary
Summary {
#[arg(short, long)]
group_by: Option<String>,
},
/// Create new item
Create {
name: String,
#[arg(short, long)]
description: Option<String>,
},
/// Show version
Version,
}
```
#### Logging
Structured logging with tracing:
```rust
// Initialize
let log_level = if cli.verbose { "debug" } else { "info" };
tracing_subscriber::fmt()
.with_env_filter(log_level)
.init();
// Usage
tracing::info!("Starting operation");
tracing::debug!("Debug info");
tracing::warn!("Warning message");
tracing::error!("Error occurred");
```
### Data Flow
```
User runs command
clap parses arguments
Config discovery (tools-shared)
Load configuration (serde + TOML)
Validate configuration
Initialize service from core library
Execute command (call service method)
Format output (JSON, table, text)
Display to user
```
## Configuration System
### Configuration File (TOML)
**Structure**:
```toml
[{{tool_name_kebab}}]
# Main settings
setting1 = "value1"
setting2 = 42
enable_feature = true
[{{tool_name_kebab}}.advanced]
# Advanced options
max_items = 1000
timeout_seconds = 30
[database]
# If using database
type = "sqlite"
path = ".project/{{tool_name_kebab}}.db"
```
### Configuration Validation
In CLI before execution:
```rust
fn validate_config(config: &Config) -> Result<()> {
if config.name.is_empty() {
return Err(ConfigError::EmptyName);
}
if config.timeout < 0 {
return Err(ConfigError::InvalidTimeout);
}
Ok(())
}
```
### Environment Variable Overrides
Support ENV vars for key settings:
```bash
export {{TOOL_NAME}}_SETTING1=value1
export {{TOOL_NAME}}_TIMEOUT=60
{{tool_name_kebab}} list
# Uses ENV values if set, otherwise config file
```
## Data Persistence
### Storage Interface
```rust
pub trait Storage {
async fn create(&mut self, item: {{MainType}}) -> Result<()>;
async fn get(&self, id: &str) -> Result<{{MainType}}>;
async fn list(&self) -> Result<Vec<{{MainType}}>
async fn update(&mut self, item: {{MainType}}) -> Result<()>;
async fn delete(&mut self, id: &str) -> Result<()>;
}
```
### Storage Implementations
**In-Memory** (template default):
- Fast for development
- Data lost on restart
- Thread-safe with Arc<Mutex<>>
**SQLite**:
- File-based, embedded
- Good for single-user tools
- Use sqlx with async
**PostgreSQL**:
- Server-based
- Multiple concurrent users
- Use sqlx with connection pooling
## Error Propagation
```
Tier 3 (CLI)
↓ catches
Tier 2 (API)
↓ catches
Tier 1 (Core)
↓ propagates
Result<T, Error>
↓ back up
Format for user/response
```
## Testing Strategy
### Unit Tests (Tier 1)
- Test each service method
- Mock storage
- Test error conditions
- Minimum 15 tests
### Integration Tests
- Test with real storage
- Test full workflows
- Test configuration loading
- Test CLI argument parsing
### End-to-End Tests
- Test complete user workflows
- Test all commands
- Test error scenarios
- Test configuration discovery
## Performance Considerations
### Optimization Points
- Configuration caching
- Lazy loading of items
- Database connection pooling
- Async/await for all I/O
### Bottlenecks to Avoid
- Synchronous blocking I/O
- Loading all items in memory
- Repeated configuration parsing
- Unnecessary cloning
### Monitoring
- Structured logging (tracing)
- Performance metrics
- Error tracking
- Resource usage
## Security Considerations
### Input Validation
- Validate all user input
- Sanitize configuration values
- Validate file paths
- Check for path traversal
### Error Handling
- Don't expose internal paths
- Don't leak sensitive data
- Provide helpful error messages
- Log with appropriate levels
### Dependencies
- Audit external crates (cargo audit)
- Keep dependencies updated
- Minimize external unsafe code
- Pin versions in production
## Extension Points
### Adding New Commands
1. Add variant to Commands enum
2. Implement handler
3. Add tests
4. Update help text
5. Update documentation
### Adding New Service Methods
1. Implement in {{MainType}}Service
2. Add error handling
3. Add tests (success and error paths)
4. Update documentation
5. Expose in CLI or API
### Adding New Storage Backend
1. Implement Storage trait
2. Add configuration option
3. Add feature flag
4. Add tests
5. Update documentation
## Deployment Checklist
- ✅ All tests pass (cargo test --workspace)
- ✅ No clippy warnings (cargo clippy)
- ✅ Code formatted (cargo fmt)
- ✅ No vulnerabilities (cargo audit)
- ✅ Documentation complete
- ✅ Help text clear and accurate
- ✅ Configuration discoverable
- ✅ Error messages helpful
## References
- **Complete README**: [README.md](README.md)
- **Quick Start**: [QUICKSTART.md](QUICKSTART.md)
- **Configuration Guide**: [configuration.md](configuration.md)
- **Code**: `crates/{{tool_name_kebab}}-*/src/`