8.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
RoadWave is a geo-localized audio social network for road users (drivers, pedestrians, tourists). Users listen to audio content (podcasts, audio guides, ads, live radio) based on their geographic location and interests.
Tech Stack:
- Backend: Go 1.21+ with Fiber framework
- Mobile: Flutter (see ADR-014)
- Database: PostgreSQL 16+ with PostGIS extension
- Cache: Redis 7+ with geospatial features
- Auth: Zitadel (self-hosted IAM)
- Data Access: sqlc for type-safe SQL code generation
- Streaming: HLS protocol with Opus codec
Monorepo Structure
This is a monorepo organized as follows:
/backend → Go backend API (modular monolith)
/mobile → Flutter mobile app
/features → Shared BDD Gherkin features (test specs)
/docs/adr → Architecture Decision Records
/shared → Shared code and API contracts
/docker → Docker configuration files
Important: BDD test features (.feature files) are shared in /features, but each component implements its own step definitions:
- Backend step definitions:
backend/tests/bdd/ - Mobile step definitions:
mobile/tests/bdd/
See ADR-016 for monorepo organization rationale.
Backend Architecture
Modular monolith with clear module separation (ADR-012):
backend/internal/
├── auth/ # JWT validation, Zitadel integration
├── user/ # User profiles, interest gauges
├── content/ # Content CRUD, metadata
├── geo/ # Geospatial search, recommendation algorithm
├── streaming/ # HLS generation, transcoding
├── moderation/ # Content moderation, reporting workflow
├── payment/ # Mangopay integration
└── analytics/ # Listening metrics, interest gauge evolution
Module pattern: Each module follows handler.go → service.go → repository.go.
Database access: Uses sqlc (ADR-013) for type-safe Go code generation from SQL queries. This allows writing complex PostGIS spatial queries while maintaining compile-time type safety.
Development Commands
IMPORTANT: Always use docker compose (not docker-compose) as per user preferences.
All commands must be run from the monorepo root:
Development
make init # Initialize project (install tools, setup .env)
make dev # Start backend with hot reload (Air)
make docker-up # Start all services (API, PostgreSQL, Redis, Zitadel, Adminer)
make docker-down # Stop all Docker services
make docker-logs # Show Docker logs
Services after make docker-up:
- API: http://localhost:8080
- Zitadel: http://localhost:8081
- Adminer: http://localhost:8082
Testing
Test Strategy (ADR-015):
- Unit tests: Testify (80%+ coverage target)
- Integration tests: Testcontainers (for PostGIS queries)
- BDD tests: Godog/Gherkin (user stories validation)
make test # Run all tests (unit + integration + BDD)
make test-unit # Unit tests only (fast, ~30s)
make test-integration # Integration tests with Testcontainers
make test-bdd # BDD tests with Godog (Gherkin features)
make test-coverage # Generate coverage report (coverage.html)
Running a single test:
cd backend
go test -v -run TestFunctionName ./path/to/package
Running a single BDD feature:
godog run features/path/to/feature.feature
Database
make migrate-up # Apply all migrations
make migrate-down # Rollback last migration
make migrate-create name=add_users # Create new migration
make migrate-version # Show current migration version
make sqlc-generate # Generate Go code from SQL queries
After modifying SQL queries in backend/queries/*.sql, always run make sqlc-generate to regenerate Go code.
Build & Code Quality
make build # Build production binary (backend/bin/api)
make lint # Run golangci-lint
make format # Format Go code (gofmt)
make deps # Download and tidy Go dependencies
make clean # Clean build artifacts
Documentation
make docs-serve # Generate BDD docs and serve MkDocs (http://localhost:8000)
make bdd-docs # Same as docs-serve
make docs-pdf # Generate PDF of all documentation
make docs-clean # Remove generated docs and PDF
Working with sqlc
When adding or modifying database queries:
- Write SQL query in
backend/queries/*.sql:
-- name: GetContentNearby :many
SELECT id, title, ST_Distance(location, $1::geography) as distance
FROM contents
WHERE ST_DWithin(location, $1::geography, $2)
ORDER BY distance
LIMIT $3;
- Run code generation:
make sqlc-generate
- Use generated type-safe Go code:
contents, err := q.GetContentNearby(ctx, location, radius, limit)
Writing BDD Tests
BDD features use Gherkin syntax (ADR-007):
- Write feature in
/features/<domain>/<feature>.feature - Implement step definitions in
backend/tests/bdd/for API testing - Run with
make test-bdd
Example feature:
Feature: Geolocalised recommendation
Scenario: Tourist near a monument
Given a user with "tourism" interest at 80%
And a GPS position 100m from the Eiffel Tower
When the system calculates recommendations
Then the audio guide "Histoire de la Tour Eiffel" is in first position
Key Architectural Decisions
All technical decisions are documented in Architecture Decision Records (ADRs) in /docs/adr/:
- ADR-001: Backend language (Go)
- ADR-002: Streaming protocol (HLS)
- ADR-005: Database (PostgreSQL + PostGIS)
- ADR-008: Authentication (Zitadel)
- ADR-012: Backend architecture (modular monolith)
- ADR-013: Data access (sqlc)
- ADR-016: Monorepo organization
When making architectural decisions, check if there's an existing ADR or create a new one following the established pattern.
Recommendation Algorithm
Core feature combining geolocation and interest matching:
- Geographic priority: GPS point > city > department > region > country
- Interest gauges: Dynamic scores per category (automobile, travel, music, etc.)
- Combined scoring: Distance + interest matching
- Cache: Redis geospatial for performance (
GEORADIUS)
The algorithm is implemented in backend/internal/geo/ and uses PostGIS functions like ST_DWithin, ST_Distance.
Common Patterns
Module Structure
Each backend module follows this pattern:
internal/modulename/
├── handler.go # HTTP handlers (Fiber routes)
├── service.go # Business logic
├── repository.go # Database access (sqlc generated code)
└── models.go # Domain models (if needed)
Error Handling
Use the custom error package in backend/pkg/errors/ for consistent error handling across the application.
Configuration
Environment variables are loaded via backend/pkg/config/. Development config is in backend/.env (copied from .env.example during make init).
PostGIS Spatial Queries
Example patterns for geospatial operations:
-- Find content within radius
WHERE ST_DWithin(location::geography, ST_MakePoint($lon, $lat)::geography, $radius_meters)
-- Calculate distance
SELECT ST_Distance(location::geography, ST_MakePoint($lon, $lat)::geography) as distance
-- Order by proximity
ORDER BY location <-> ST_MakePoint($lon, $lat)::geography
Testing with Testcontainers
Integration tests that require PostGIS use Testcontainers to spin up a real PostgreSQL+PostGIS instance:
// backend/tests/integration/geo_test.go
// Uses testcontainers to validate complex spatial queries
This ensures PostGIS queries work correctly without mocking.
Authentication Flow
Zitadel handles authentication (ADR-008):
- Self-hosted on OVH (France data sovereignty)
- OAuth2 PKCE for mobile apps
- JWT validation in backend using zitadel-go SDK
- Shares PostgreSQL database with RoadWave (separate schema)
Performance Targets
See TECHNICAL.md for detailed metrics:
- API latency p99: < 100ms
- Audio start time: < 3s
- Target availability: 99.9%
- Concurrent connections/server: 100K+