Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Feature 00: Project Foundation

Overview

Set up the Rust Cargo workspace, database infrastructure, Docker Compose development environment, and CI pipeline. This is the skeleton that every subsequent feature builds on — no game logic yet, just the scaffolding to build, run, test, and deploy.

Technical Tasks

1. Initialize Cargo Workspace

  • Create root Cargo.toml with workspace members
  • Create crate stubs:
    • crates/api — binary, depends on db, types, game, jobs
    • crates/workers — binary, depends on db, types, game, jobs
    • crates/game — library
    • crates/db — library
    • crates/types — library
    • crates/jobs — library
  • Add shared dependencies to workspace Cargo.toml:
    • tokio (async runtime)
    • serde, serde_json (serialization)
    • sqlx (database, with postgres, runtime-tokio, tls-rustls features)
    • redis (with tokio-comp feature)
    • uuid (with v4, serde features)
    • chrono (timestamps, with serde feature)
    • tracing, tracing-subscriber (logging)
    • thiserror, anyhow (error handling)
  • Verify cargo build succeeds with empty crates
  • Verify cargo test runs (no tests yet, but harness works)

2. API Server Skeleton (crates/api)

  • Add Axum dependencies: axum, tower, tower-http (cors, tracing, compression)
  • Create main.rs with:
    • Tokio runtime initialization
    • Tracing subscriber setup (JSON format for structured logging)
    • Axum router with a health check endpoint: GET /api/health{ "status": "ok" }
    • Graceful shutdown on SIGTERM/SIGINT
  • Create AppState struct holding:
    • PgPool (SQLx connection pool)
    • redis::Client (Redis connection)
  • Wire AppState into Axum via Extension or State
  • Create src/error.rs — unified error type that converts to Axum responses with appropriate HTTP status codes
  • Create src/middleware/ directory with placeholder modules

3. Worker Binary Skeleton (crates/workers)

  • Create main.rs with:
    • Tokio runtime initialization
    • Tracing subscriber setup
    • CLI argument parsing (e.g., --scheduler flag) via clap
    • Placeholder job processing loop: poll Redis, log “no jobs”, sleep
    • Graceful shutdown

4. Database Setup (crates/db)

  • Add SQLx CLI as a dev dependency or document cargo install sqlx-cli
  • Create migrations/ directory
  • Create initial migration 0001_create_extensions.sql:
    • CREATE EXTENSION IF NOT EXISTS "pgcrypto"; (for gen_random_uuid())
  • Create src/lib.rs:
    • create_pool(database_url: &str) -> PgPool function
    • run_migrations(pool: &PgPool) function
  • Create src/models/mod.rs — empty, will hold row types
  • Create src/queries/mod.rs — empty, will hold query functions

5. Job Queue Foundation (crates/jobs)

  • Create Redis-backed delayed job queue:
    • enqueue(queue: &str, payload: &[u8], delay: Duration) — adds job to Redis sorted set with score = now + delay
    • enqueue_immediate(queue: &str, payload: &[u8]) — adds with score = now
    • poll(queue: &str) -> Option<Job>ZPOPMIN where score <= now
    • ack(job_id: &str) — remove from processing set
    • fail(job_id: &str, error: &str) — move to dead letter queue
  • Job struct: { id: String, queue: String, payload: Vec<u8>, enqueued_at: DateTime, attempts: u32 }
  • Use Redis ZADD/ZPOPMIN with timestamps as scores for delayed execution
  • Add retry logic: on failure, re-enqueue with exponential backoff (max 3 retries)

6. Docker Compose Development Environment

  • Create docker-compose.yml:
    • PostgreSQL 16 Alpine (port 5432, volume for persistence)
    • Valkey 7 Alpine (port 6379)
    • Mailpit (ports 8025 web UI, 1025 SMTP)
  • Create .env.example with:
    • DATABASE_URL=postgres://delve:devpassword@localhost:5432/delve
    • REDIS_URL=redis://localhost:6379
    • SMTP_HOST=localhost
    • SMTP_PORT=1025
  • Add .env to .gitignore

7. Configuration Management

  • Create crates/api/src/config.rs:
    • Load config from environment variables (with dotenvy for .env files in dev)
    • Struct: Config { database_url, redis_url, host, port, smtp_host, smtp_port }
    • Validate required fields on startup, panic with clear error if missing

8. CI Pipeline

  • Create .github/workflows/ci.yml:
    • Trigger on PR and push to main
    • Steps: cargo fmt --check, cargo clippy, cargo test, cargo build --release
    • Use actions-rs/toolchain or dtolnay/rust-toolchain
    • Cache cargo registry and target directory via Swatinem/rust-cache
    • Start PostgreSQL and Redis services for integration tests

9. Dockerfile

  • Create multi-stage Dockerfile:
    • Stage 1: cargo-chef to cache dependency compilation
    • Stage 2: Build release binaries
    • Stage 3: FROM debian:bookworm-slim (or Alpine) — copy binaries, set entrypoint
  • Produce two targets: delve-api and delve-workers
  • Verify docker build succeeds and images are < 50MB

10. SvelteKit Client Skeleton

  • Initialize SvelteKit project in client/ directory:
    • pnpm create svelte@latest client (skeleton project, TypeScript)
    • Install: tailwindcss, @tanstack/svelte-query, @tanstack/query-sync-storage-persister
    • Configure Tailwind with design token stubs (colors for rarity tiers, etc.)
    • Configure SvelteKit static adapter (SPA mode)
    • Configure Vite proxy: /apihttp://localhost:3000 in dev
  • Create root layout (+layout.svelte):
    • QueryClientProvider wrapper
    • Placeholder navigation shell
  • Create GET /api/health query to verify client↔server connectivity
  • Create lib/api/client.ts — base fetch wrapper with auth header injection

Tests

Unit Tests

  • crates/jobs: Test enqueue + poll returns job after delay has elapsed
  • crates/jobs: Test poll returns None when delay hasn’t elapsed
  • crates/jobs: Test ack removes job from processing set
  • crates/jobs: Test fail with retry re-enqueues with backoff
  • crates/jobs: Test fail after max retries moves to dead letter queue
  • crates/db: Test create_pool connects to test database
  • crates/db: Test run_migrations applies migrations without error
  • crates/api/config: Test config loads from env vars
  • crates/api/config: Test config panics on missing required fields

Integration Tests

  • API server starts and GET /api/health returns 200 with { "status": "ok" }
  • API server connects to PostgreSQL (pool creation succeeds)
  • API server connects to Redis (ping succeeds)
  • Worker binary starts and exits cleanly on SIGTERM
  • Job queue round-trip: enqueue → poll → ack (against real Redis)
  • Migrations run cleanly on a fresh database

Client Tests

  • SvelteKit builds without errors (pnpm build)
  • Health check query hits the API and receives a response (dev proxy test)