|
| 1 | +# Architecture |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +TrendWeight uses a modern web architecture with a C# ASP.NET Core API backend and a Vite + React frontend. |
| 6 | + |
| 7 | +## Backend Architecture |
| 8 | + |
| 9 | +### Technology Stack |
| 10 | +- **ASP.NET Core 9.0** Web API |
| 11 | +- **Supabase** (PostgreSQL) for data storage |
| 12 | +- **JWT-based authentication** |
| 13 | +- **Feature-based architecture** with service layer pattern |
| 14 | + |
| 15 | +### Service Layer Pattern |
| 16 | + |
| 17 | +The backend uses a service layer pattern to separate business logic from controllers: |
| 18 | +- **ProfileService**: Manages user profile CRUD operations and business logic |
| 19 | +- **ProviderService**: Base class for provider-specific implementations (Withings/Fitbit) |
| 20 | +- **SourceDataService**: Handles raw measurement data storage and retrieval |
| 21 | +- **MeasurementsController**: Orchestrates data fetching from multiple providers |
| 22 | + |
| 23 | +Controllers should be thin, delegating complex logic to services. |
| 24 | + |
| 25 | +### Project Structure |
| 26 | +``` |
| 27 | +apps/api/ |
| 28 | +└── TrendWeight/ |
| 29 | + ├── Features/ # Feature-based organization |
| 30 | + │ ├── Profile/ # User profile management |
| 31 | + │ ├── Providers/ # Withings/Fitbit integrations |
| 32 | + │ └── Measurements/ # Weight data management |
| 33 | + └── Infrastructure/ # Cross-cutting concerns |
| 34 | +``` |
| 35 | + |
| 36 | +## Frontend Architecture |
| 37 | + |
| 38 | +### Technology Stack |
| 39 | +- **React 19** with TypeScript |
| 40 | +- **Vite** for build tooling |
| 41 | +- **TanStack Router** for routing |
| 42 | +- **TanStack Query** for server state management |
| 43 | +- **Tailwind CSS v4** for styling |
| 44 | +- **Radix UI** for accessible components |
| 45 | + |
| 46 | +## Timestamp Storage |
| 47 | + |
| 48 | +The application stores weight measurements with local date and time strings rather than Unix timestamps: |
| 49 | + |
| 50 | +### Storage Format |
| 51 | +```typescript |
| 52 | +interface RawMeasurement { |
| 53 | + date: string; // "2024-01-15" (YYYY-MM-DD) |
| 54 | + time: string; // "06:30:00" (HH:mm:ss) |
| 55 | + weight: number; // kg |
| 56 | + fatRatio?: number; // 0-1 ratio |
| 57 | +} |
| 58 | +``` |
| 59 | + |
| 60 | +### Provider Handling |
| 61 | +- **Withings**: Provides UTC timestamps with timezone info. The backend converts these to local time before storage. |
| 62 | +- **Fitbit**: Provides measurements already in the user's local timezone, stored as-is. |
| 63 | + |
| 64 | +This approach: |
| 65 | +- Preserves the user's local time context |
| 66 | +- Avoids timezone conversion issues |
| 67 | +- Simplifies display logic on the frontend |
| 68 | +- Handles provider differences transparently |
| 69 | + |
| 70 | +## Database Schema |
| 71 | + |
| 72 | +### Tables |
| 73 | +- **profiles**: User profiles with settings (UUID primary key) |
| 74 | +- **provider_links**: OAuth tokens for provider integrations |
| 75 | +- **source_data**: Raw measurement data from providers |
| 76 | + |
| 77 | +### Key Principles |
| 78 | +- All timestamps stored as ISO 8601 strings |
| 79 | +- All weights stored in kg for consistency |
| 80 | +- JSONB columns for flexible data storage |
| 81 | +- No unique constraint on email (uses Supabase Auth UID) |
| 82 | + |
| 83 | +## Authentication Flow |
| 84 | + |
| 85 | +1. Frontend uses Supabase Auth for user authentication |
| 86 | +2. Backend validates Supabase JWTs using shared JWT secret |
| 87 | +3. All API endpoints require authentication except health checks and OAuth callbacks |
| 88 | +4. Provider OAuth tokens stored encrypted in database |
| 89 | + |
| 90 | +## Development Guidelines |
| 91 | + |
| 92 | +### Backend |
| 93 | +- Feature folders for organization |
| 94 | +- Dependency injection for all services |
| 95 | +- Async/await for all I/O operations |
| 96 | +- Nullable fields should remain null when not provided (never default to 0) |
| 97 | + |
| 98 | +### Frontend |
| 99 | +- Functional components with hooks |
| 100 | +- Custom hooks for reusable logic |
| 101 | +- Error boundaries for graceful error handling |
| 102 | +- Route-level authentication guards |
| 103 | + |
| 104 | +## Provider Integration Notes |
| 105 | + |
| 106 | +### Fitbit Limitations |
| 107 | +- Weight data requests cannot start before 2009-01-01 |
| 108 | +- Maximum date range per request is 32 days |
| 109 | +- Always use initial sync date of 2009-01-01 |
| 110 | + |
| 111 | +### Token Management |
| 112 | +- Tokens automatically refresh when expired |
| 113 | +- Provider userid fields vary in type and are not used |
| 114 | +- Failed auth triggers reconnection flow |
0 commit comments