nextjs
Dataarchitecture

🎯 Data Architecture Fix: From Hybrid to Database-First

8/4/2025 Accurate

Problem: Hybrid Data Architecture

The Issue You Identified

Components were fetching from two different data sources without knowing the implications:

πŸ”΄ API Components (Stale Data)      🟒 Database Components (Fresh Data)
β”œβ”€ AthleteHeader                   β”œβ”€ RecentActivities  
β”œβ”€ useAthleteData()                β”œβ”€ ActivitiesDashboard
└─ Hits Strava API                 β”œβ”€ ActivityFeed
                                   β”œβ”€ TrainingLoadChart
                                   └─ useUserActivities()

What Happens:

  1. Sync Process β†’ Updates database βœ…
  2. Database components β†’ Show new data immediately βœ…
  3. API components β†’ Still show old cached data ❌

Your 4-Hour Run Missing:

  • βœ… Sync worked β†’ Run stored in database
  • βœ… Dashboard shows it β†’ Uses useUserActivities()
  • ❌ Header shows old name β†’ Uses useAthleteData() from API

Solution: Database-First Architecture

Fixed Components:

  • βœ… AthleteHeader β†’ Now uses useAthleteProfile(userId)
  • βœ… RecentActivities β†’ Already using useUserActivities(userId)
  • βœ… ActivitiesDashboard β†’ Already using useUserActivities(userId)
  • βœ… TrainingLoadChart β†’ Already using database via useUserActivities(userId)

Data Flow Now:

Strava API β†’ Sync Service β†’ Database β†’ React Query β†’ Components
                    ↑                      ↑
              (Once per sync)        (Cached, fast)

Benefits:

  • Consistency: All components use same data source
  • Speed: Database queries are faster than API calls
  • No Rate Limits: No more 600 requests/day limit
  • Real-time: Sync updates all components instantly

Migration Checklist

βœ… Completed:

  • Created useAthleteProfile(userId) hook
  • Updated AthleteHeader to use database
  • All activity components use useUserActivities(userId)
  • Training load components use database

πŸ”„ To Deprecate (Optional):

  • useAthleteData() - Only used by old components
  • useAthleteActivities() - Superseded by useUserActivities()
  • /api/strava/athlete route - No longer needed

🎯 Result:

Single Source of Truth β†’ Database stores everything, components read from there.

Testing the Fix

  1. Sync your data:

    # In browser console on /dashboard
    fetch('/api/strava/sync', { method: 'POST' })
  2. Verify all components update:

    • Athlete header shows your name βœ…
    • Recent activities show latest run βœ…
    • Training load includes new data βœ…
    • All data consistent across components βœ…

Best Practices Going Forward

1. Always Use Database-First Hooks:

// βœ… Good: Database-first
const { data: activities } = useUserActivities(userId)
const { data: athlete } = useAthleteProfile(userId)
 
// ❌ Avoid: API-first
const { data: activities } = useAthleteActivities(token)
const { data: athlete } = useAthleteData(token)

2. Sync Strategy:

  • Sync once β†’ Updates database
  • Components read β†’ From database (cached)
  • Fast, consistent, no rate limits

3. Query Invalidation:

// When sync completes, invalidate related queries
queryClient.invalidateQueries({ queryKey: ['user', 'activities'] })
queryClient.invalidateQueries({ queryKey: ['athlete', 'profile'] })

Architecture Principle

"Sync to Database, Read from Database"

Never mix API and database reads in the same app. Pick one source of truth and stick to it.