Files
roadwave/CLAUDE.md
2026-01-31 11:45:11 +01:00

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.goservice.gorepository.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:

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:

  1. 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;
  1. Run code generation:
make sqlc-generate
  1. Use generated type-safe Go code:
contents, err := q.GetContentNearby(ctx, location, radius, limit)

Writing BDD Tests

BDD features use Gherkin syntax (ADR-007):

  1. Write feature in /features/<domain>/<feature>.feature
  2. Implement step definitions in backend/tests/bdd/ for API testing
  3. 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+