SKILL.md
Cloud-Native Domain
Layer 3: Domain Constraints
Domain Constraints → Design Implications
Domain Rule
Design Constraint
Rust Implication
12-Factor
Config from env
Environment-based config
Observability
Metrics + traces
tracing + opentelemetry
Health checks
Liveness/readiness
Dedicated endpoints
Graceful shutdown
Clean termination
Signal handling
Horizontal scale
Stateless design
No local state
Container-friendly
Small binaries
Release optimization
Critical Constraints
Stateless Design
RULE: No local persistent state
WHY: Pods can be killed/rescheduled anytime
RUST: External state (Redis, DB), no static mut
Graceful Shutdown
RULE: Handle SIGTERM, drain connections
WHY: Zero-downtime deployments
RUST: tokio::signal + graceful shutdown
Observability
RULE: Every request must be traceable
WHY: Debugging distributed systems
RUST: tracing spans, opentelemetry export
Trace Down ↓
From constraints to design (Layer 2):
"Need distributed tracing"
↓ m12-lifecycle: Span lifecycle
↓ tracing + opentelemetry
"Need graceful shutdown"
↓ m07-concurrency: Signal handling
↓ m12-lifecycle: Connection draining
"Need health checks"
↓ domain-web: HTTP endpoints
↓ m06-error-handling: Health status
Key Crates
Purpose
Crate
gRPC
tonic
Kubernetes
kube, kube-runtime
Docker
bollard
Tracing
tracing, opentelemetry
Metrics
prometheus, metrics
Config
config, figment
Health
HTTP endpoints
Design Patterns
Pattern
Purpose
Implementation
gRPC services
Service mesh
tonic + tower
K8s operators
Custom resources
kube-runtime Controller
Observability
Debugging
tracing + OTEL
Health checks
Orchestration
/health, /ready
Config
12-factor
Env vars + secrets
Code Pattern: Graceful Shutdown
use tokio::signal;
async fn run_server() -> anyhow::Result<()> {
let app = Router::new()
.route("/health", get(health))
.route("/ready", get(ready));
let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.with_graceful_shutdown(shutdown_signal())
.await?;
Ok(())
}
async fn shutdown_signal() {
signal::ctrl_c().await.expect("failed to listen for ctrl+c");
tracing::info!("shutdown signal received");
}
Health Check Pattern
async fn health() -> StatusCode {
StatusCode::OK
}
async fn ready(State(db): State<Arc<DbPool>>) -> StatusCode {
match db.ping().await {
Ok(_) => StatusCode::OK,
Err(_) => StatusCode::SERVICE_UNAVAILABLE,
}
}
Common Mistakes
Mistake
Domain Violation
Fix
Local file state
Not stateless
External storage
No SIGTERM handling
Hard kills
Graceful shutdown
No tracing
Can't debug
tracing spans
Static config
Not 12-factor
Env vars
Trace to Layer 1
Constraint
Layer 2 Pattern
Layer 1 Implementation
Stateless
External state
Arc for external
Graceful shutdown
Signal handling
tokio::signal
Tracing
Span lifecycle
tracing + OTEL
Health checks
HTTP endpoints
Dedicated routes
Related Skills
When
See
Async patterns
m07-concurrency
HTTP endpoints
domain-web
Error handling
m13-domain-error
Resource lifecycle
m12-lifecycle