Skip to content

Latest commit

 

History

History
325 lines (260 loc) · 9.87 KB

File metadata and controls

325 lines (260 loc) · 9.87 KB

Architecture Documentation

System Overview

F1 Visor is a single-page application (SPA) built with React that visualizes Formula 1 race data from the OpenF1 API. The application follows a modular architecture with clear separation of concerns.

Architecture Diagram

┌─────────────────────────────────────────────────────────┐
│                     Browser (Client)                     │
├─────────────────────────────────────────────────────────┤
│                                                           │
│  ┌─────────────────────────────────────────────────┐   │
│  │              React Application                   │   │
│  │                                                   │   │
│  │  ┌──────────────┐  ┌──────────────┐            │   │
│  │  │  Components  │  │    Stores    │            │   │
│  │  │   (View)     │◄─┤   (Zustand)  │            │   │
│  │  └──────┬───────┘  └──────────────┘            │   │
│  │         │                                        │   │
│  │         ▼                                        │   │
│  │  ┌──────────────┐  ┌──────────────┐            │   │
│  │  │ Custom Hooks │◄─┤ React Query  │            │   │
│  │  └──────┬───────┘  └──────┬───────┘            │   │
│  │         │                  │                     │   │
│  │         ▼                  ▼                     │   │
│  │  ┌─────────────────────────────────┐            │   │
│  │  │      API Services (Axios)       │            │   │
│  │  └─────────────┬───────────────────┘            │   │
│  └────────────────┼──────────────────────────────┘   │
│                   │                                    │
└───────────────────┼────────────────────────────────────┘
                    │
                    │ HTTPS
                    ▼
         ┌──────────────────────┐
         │   OpenF1 API         │
         │  api.openf1.org/v1   │
         └──────────────────────┘

Core Layers

1. Presentation Layer (Components)

Location: src/components/

Responsible for rendering UI and handling user interactions.

Component Categories:

  • Circuit Components (circuit/)

    • CircuitMap.tsx - Canvas-based 2D circuit renderer
    • CarMarker.tsx - Individual car position marker
    • CircuitControls.tsx - Zoom/pan controls
  • Telemetry Components (telemetry/)

    • TelemetryPanel.tsx - Main telemetry container
    • SpeedGauge.tsx - Speed indicator
    • RPMGauge.tsx - RPM indicator
    • GearDisplay.tsx - Current gear display
    • SpeedChart.tsx - Speed over time graph
  • Position Components (positions/)

    • PositionsTable.tsx - Driver standings table
    • DriverRow.tsx - Individual driver row
    • IntervalDisplay.tsx - Time gap display
  • Session Components (session/)

    • SessionSelector.tsx - Session picker
    • SeasonFilter.tsx - Year selector
    • CircuitFilter.tsx - Circuit selector
  • UI Components (ui/)

    • Button.tsx - Reusable button
    • Card.tsx - Container card
    • Loading.tsx - Loading spinner
    • ErrorMessage.tsx - Error display

2. State Management Layer

Location: src/stores/

Uses Zustand for global state management.

Stores:

// raceStore.ts
interface RaceStore {
  selectedDriver: number | null
  playbackSpeed: number
  currentTime: Date | null
  isPlaying: boolean
  selectedSession: number | null
}

// uiStore.ts
interface UIStore {
  sidebarOpen: boolean
  selectedTab: 'telemetry' | 'positions' | 'strategy'
  mapZoom: number
}

3. Data Fetching Layer

Location: src/hooks/ and src/services/

Custom Hooks (React Query integration):

  • useSessionData() - Fetch session metadata
  • useLocationData() - Fetch GPS coordinates
  • usePositionData() - Fetch race positions
  • useLapData() - Fetch lap times
  • useTelemetryData() - Fetch car telemetry
  • usePitData() - Fetch pit stops

API Services:

// services/openf1/client.ts
export const apiClient = axios.create({
  baseURL: 'https://api.openf1.org/v1',
  timeout: 10000,
})

// services/openf1/sessions.ts
export const getSessions = (filters: SessionFilters) => {...}
export const getSession = (sessionKey: number) => {...}

// services/openf1/location.ts
export const getLocationData = (sessionKey: number, filters: LocationFilters) => {...}

// ... other service modules

4. Utility Layer

Location: src/utils/

Pure functions for data transformation and calculations.

Key Utilities:

  • coordinates.ts - GPS to canvas conversion

    export const gpsToCanvas = (lat: number, lng: number, bounds: Bounds): Point
    export const calculateBounds = (locations: Location[]): Bounds
  • time.ts - Time manipulation

    export const parseRaceTime = (timeString: string): Date
    export const formatLapTime = (milliseconds: number): string
    export const interpolateTime = (start: Date, end: Date, progress: number): Date
  • colors.ts - Team color mapping

    export const getTeamColor = (teamName: string): string

5. Type Definitions

Location: src/types/

TypeScript interfaces and types for the entire application.

// types/openf1.ts
export interface Session {
  session_key: number
  session_name: string
  date_start: string
  date_end: string
  circuit_short_name: string
}

export interface LocationData {
  date: string
  driver_number: number
  lat: number
  lng: number
  x: number
  y: number
  z: number
}

// ... other types

Data Flow

1. Initial Load

User Opens App
    ↓
SessionSelector Component Mounts
    ↓
useSessionData Hook Fetches Sessions
    ↓
React Query Caches Results
    ↓
User Selects Session
    ↓
Store Updates (selectedSession)

2. Race Playback

User Clicks Play
    ↓
Store Updates (isPlaying: true)
    ↓
Playback Loop Starts (useEffect)
    ↓
currentTime Increments
    ↓
Components React to Time Change
    ↓
  ├─► CircuitMap: Fetches location data for currentTime
  ├─► TelemetryPanel: Fetches car_data for currentTime
  ├─► PositionsTable: Fetches position data for currentTime
  └─► PitStopsPanel: Checks for pit stops at currentTime
    ↓
Canvas Re-renders with New Positions

3. Data Caching Strategy

React Query handles caching with the following strategy:

  • Stale Time: 5 minutes (data doesn't change)
  • Cache Time: 10 minutes
  • Refetch on Window Focus: Disabled (historical data)
  • Retry: 1 attempt

Performance Considerations

1. Canvas Rendering

  • Use requestAnimationFrame for smooth animations
  • Implement dirty checking to avoid unnecessary redraws
  • Use off-screen canvas for complex drawings

2. Data Fetching

  • Batch API requests when possible
  • Use time-based filtering to reduce payload size
  • Implement virtual scrolling for large lists

3. State Updates

  • Minimize re-renders with proper memoization
  • Use useMemo for expensive calculations
  • Use useCallback for event handlers

4. Code Splitting

  • Lazy load heavy components (charts, maps)
  • Split by route if multiple pages added
  • Dynamic imports for large libraries

Security Considerations

  • No authentication required (public API)
  • HTTPS only for API calls
  • No sensitive data stored
  • CORS handled by OpenF1 API

Scalability

Current Limitations:

  • Client-side only (no backend)
  • Limited by browser memory for large datasets
  • No real-time WebSocket support yet

Future Enhancements:

  • Add service worker for offline support
  • Implement WebSocket for live data
  • Add backend proxy for API rate limiting
  • Database for user preferences

Technology Decisions

Why React?

  • Component-based architecture
  • Large ecosystem
  • Excellent TypeScript support
  • Virtual DOM for performance

Why Zustand over Redux?

  • Simpler API
  • Less boilerplate
  • Better TypeScript inference
  • Smaller bundle size

Why React Query?

  • Built-in caching
  • Automatic refetching
  • Loading/error states
  • Optimistic updates

Why Canvas over SVG?

  • Better performance for many elements (20 cars)
  • Easier animation with requestAnimationFrame
  • Lower memory footprint
  • Simpler coordinate transformations

Development Workflow

  1. Feature Development

    • Create types in src/types/
    • Implement API service in src/services/
    • Create custom hook in src/hooks/
    • Build component in src/components/
    • Update store if needed
  2. Testing Strategy

    • Unit tests for utilities
    • Integration tests for API services
    • Component tests with React Testing Library
    • E2E tests for critical flows (future)
  3. Documentation

    • Update AGENTS.md for new patterns
    • Update README.md for new features
    • Add JSDoc comments for complex functions
    • Update this ARCHITECTURE.md for structural changes