Vapora/docs/adrs/0002-axum-backend.md

118 lines
3.3 KiB
Markdown
Raw Normal View History

# ADR-002: Axum como Backend Framework
**Status**: Accepted | Implemented
**Date**: 2024-11-01
**Deciders**: Backend Architecture Team
**Technical Story**: Selecting REST API framework with optimal async/middleware composition for Tokio ecosystem
---
## Decision
Usar **Axum 0.8.6** como framework REST API (no Actix-Web, no Rocket) para exponer 40+ endpoints de VAPORA.
---
## Rationale
1. **Composable Middleware**: Tower ecosystem provides first-class composable middleware patterns
2. **Type-Safe Routing**: Router defined as strong types (not string-based paths)
3. **Tokio Ecosystem**: Built directly on Tokio (not abstraction layer), enabling precise async control
4. **Extractors**: Powerful extractor system (`Json`, `State`, `Path`, custom extractors) reduces boilerplate
5. **Performance**: Zero-copy response bodies, streaming support, minimal overhead
---
## Alternatives Considered
### ❌ Actix-Web
- Mature framework with larger ecosystem
- **Cons**: Actor model adds complexity, different async patterns than Tokio, harder to integrate with Tokio primitives
### ❌ Rocket
- Developer-friendly API
- **Cons**: Synchronous-first (async as afterthought), less composable, worse error handling
### ✅ Axum (CHOSEN)
- Minimal abstraction over Tokio/Tower
- **Pros**: Composable, type-safe, Tokio-native, growing ecosystem
---
## Trade-offs
**Pros**:
- ✅ Composable middleware (Tower trait-based)
- ✅ Type-safe routing with strong types
- ✅ Zero-cost abstractions, excellent performance
- ✅ Perfect integration with Tokio async ecosystem
- ✅ Streaming responses, WebSocket support built-in
**Cons**:
- ⚠️ Smaller ecosystem than Actix-Web
- ⚠️ Steeper learning curve (requires understanding Tower traits)
- ⚠️ Fewer third-party integrations available
---
## Implementation
**Router Definition**:
```rust
let app = Router::new()
.route("/api/v1/projects", post(create_project).get(list_projects))
.route("/api/v1/projects/:id", get(get_project).put(update_project))
.route("/metrics", get(metrics_handler))
.layer(TraceLayer::new_for_http())
.layer(CorsLayer::permissive())
.layer(Extension(Arc::new(app_state)));
let listener = TcpListener::bind("0.0.0.0:8001").await?;
axum::serve(listener, app).await?;
```
**Key Files**:
- `/crates/vapora-backend/src/main.rs:126-259` (router setup)
- `/crates/vapora-backend/src/api/` (handlers)
- `/crates/vapora-backend/Cargo.toml` (dependencies)
---
## Verification
```bash
# Build backend
cargo build -p vapora-backend
# Test API endpoints
cargo test -p vapora-backend -- --nocapture
# Run server and check health
cargo run -p vapora-backend &
curl http://localhost:8001/health
curl http://localhost:8001/metrics
```
**Expected**: 40+ endpoints accessible, health check responds 200 OK, metrics endpoint returns Prometheus format
---
## Consequences
- All HTTP handling must use Axum extractors (learning curve for team)
- Request/response types must be serializable (integration with serde)
- Middleware stacking order matters (defensive against bugs)
- Easy to add WebSocket support later (Axum has built-in support)
---
## References
- [Axum Documentation](https://docs.rs/axum/)
- `/crates/vapora-backend/src/main.rs` (router definition)
- `/crates/vapora-backend/Cargo.toml` (Axum dependency)
---
**Related ADRs**: ADR-001 (Workspace), ADR-008 (Tokio)