Data Models

Université de Toulon

LIS UMR CNRS 7020

2025-03-17

Data Models

This document defines the core entities for the fitness tracking application, their relationships, and persistence considerations. These models support the requirements defined in the epics and user personas.

Core Entities Diagram

Entity Relationship Diagram

User Profile

The User Profile stores information about the application user.

data class UserProfile(
    val userId: String,             // Unique identifier
    var displayName: String,        // User's name displayed in the app
    var height: Measurement? = null, // Optional height
    var weight: Measurement? = null, // Optional current weight
    var fitnessLevel: FitnessLevel = FitnessLevel.BEGINNER,
    var weightUnit: WeightUnit = WeightUnit.KILOGRAMS,
    var distanceUnit: DistanceUnit = DistanceUnit.KILOMETERS,
    var availableEquipment: List<Equipment> = emptyList(),
    var equipmentProfiles: List<EquipmentProfile> = emptyList(),
    var dateCreated: Instant,
    var lastUpdated: Instant
)

enum class FitnessLevel {
    BEGINNER,
    INTERMEDIATE,
    ADVANCED,
    ELITE
}

enum class WeightUnit {
    KILOGRAMS,
    POUNDS
}

enum class DistanceUnit {
    KILOMETERS,
    MILES
}

data class EquipmentProfile(
    val id: String,
    var name: String,
    var equipment: List<Equipment>
)

Persistence Considerations: - Store in Room database with single row for current user - Sensitive fields (weight, height) should be optional and have privacy controls - Equipment profiles need one-to-many relationship with main profile

Exercise

Exercise defines a specific physical activity that can be performed during workouts.

data class Exercise(
    val id: String,                      // Unique identifier
    var name: String,                    // Exercise name
    var description: String,             // Detailed description
    var primaryMuscles: List<MuscleGroup>, // Primary muscles targeted
    var secondaryMuscles: List<MuscleGroup>, // Secondary muscles targeted
    var equipment: List<Equipment>,      // Equipment required
    var difficulty: ExerciseDifficulty,  // Difficulty level
    var exerciseType: ExerciseType,      // Type of exercise
    var instructions: List<String>,      // Step-by-step instructions
    var demonstrationImageUrls: List<String>, // URLs to demonstration images
    var demonstrationVideoUrl: String? = null, // URL to demonstration video
    var isCustom: Boolean,               // Whether this is a custom user exercise
    var tags: List<String>,              // Additional tags for filtering
    var isBodyweightExercise: Boolean,   // Whether the exercise uses bodyweight
    var parentExerciseId: String? = null, // For exercise variants
    var created: Instant,
    var lastUpdated: Instant
)

enum class ExerciseType {
    STRENGTH,
    CARDIO,
    FLEXIBILITY,
    BALANCE,
    PLYOMETRIC,
    COMPOUND,
    ISOLATION
}

enum class ExerciseDifficulty {
    BEGINNER,
    INTERMEDIATE,
    ADVANCED
}

enum class MuscleGroup {
    CHEST,
    BACK,
    SHOULDERS,
    BICEPS,
    TRICEPS,
    FOREARMS,
    QUADRICEPS,
    HAMSTRINGS,
    GLUTES,
    CALVES,
    CORE,
    ABDOMINALS,
    OBLIQUES,
    LOWER_BACK,
    FULL_BODY,
    CARDIO_SYSTEM
}

enum class Equipment {
    NONE,
    BARBELL,
    DUMBBELL,
    KETTLEBELL,
    RESISTANCE_BAND,
    CABLE_MACHINE,
    SMITH_MACHINE,
    SQUAT_RACK,
    BENCH,
    PULL_UP_BAR,
    DIP_STATION,
    LEG_PRESS_MACHINE,
    CARDIO_MACHINE,
    MEDICINE_BALL,
    STABILITY_BALL,
    FOAM_ROLLER,
    TRX,
    WEIGHT_PLATE
}

Persistence Considerations: - Core exercises stored in app assets as JSON for initial population - Custom exercises stored in Room database - Exercise metadata (images, videos) should support offline caching - Support efficient filtering and searches by muscle group, equipment, etc.

WorkoutProgram

WorkoutProgram represents a collection of workouts structured into a complete training program.

data class WorkoutProgram(
    val id: String,                   // Unique identifier
    var name: String,                 // Program name
    var description: String,          // Program description
    var difficulty: ProgramDifficulty, // Program difficulty
    var category: ProgramCategory,    // Program category/type
    var estimatedDurationWeeks: Int,  // Estimated duration in weeks
    var workoutsPerWeek: Int,         // Expected workouts per week
    var schedule: List<WorkoutSchedule>, // Scheduled workouts
    var isCustom: Boolean,            // Whether this is a custom user program
    var created: Instant,
    var lastUpdated: Instant,
    var creatorId: String? = null,    // ID of creator (null for system programs)
    var tags: List<String> = emptyList() // Tags for filtering programs
)

data class WorkoutSchedule(
    val workoutId: String,           // Reference to a Workout
    val weekNumber: Int,             // Week in the program (1-based)
    val dayOfWeek: Int,              // Day of week (1 = Monday, 7 = Sunday)
    val isDeloadWeek: Boolean = false // Whether this is a deload week
)

enum class ProgramDifficulty {
    BEGINNER,
    INTERMEDIATE,
    ADVANCED
}

enum class ProgramCategory {
    STRENGTH,
    HYPERTROPHY,
    ENDURANCE,
    WEIGHT_LOSS,
    GENERAL_FITNESS,
    POWERLIFTING,
    BODYBUILDING,
    CROSSFIT,
    SPORT_SPECIFIC,
    REHABILITATION
}

Persistence Considerations: - Base programs stored in app assets as JSON - Custom and modified programs stored in Room database - Support for sharing programs between users (export/import)

Workout

Workout represents a single training session with a specific set of exercises.

data class Workout(
    val id: String,                   // Unique identifier
    var name: String,                 // Workout name
    var description: String? = null,  // Workout description
    var exerciseGroups: List<ExerciseGroup>, // Grouped exercises
    var estimatedDurationMinutes: Int, // Estimated duration
    var targetMuscleGroups: List<MuscleGroup>, // Primary muscles targeted
    var difficulty: ProgramDifficulty, // Workout difficulty
    var isCustom: Boolean,            // Whether this is a custom workout
    var parentProgramId: String? = null, // Program this workout belongs to (optional)
    var created: Instant,
    var lastUpdated: Instant,
    var notes: String? = null         // Additional notes
)

data class ExerciseGroup(
    val id: String,
    var name: String? = null,         // Optional group name (e.g., "Superset 1")
    var exercises: List<WorkoutExercise>, // Exercises in this group
    var type: ExerciseGroupType       // Type of grouping
)

enum class ExerciseGroupType {
    REGULAR,      // Sequential exercises
    SUPERSET,     // Alternating exercises with minimal rest
    CIRCUIT,      // Multiple exercises performed in sequence with minimal rest
    PYRAMID       // Progressive increase/decrease in weight or reps
}

data class WorkoutExercise(
    val id: String,
    val exerciseId: String,          // Reference to an Exercise
    var order: Int,                  // Order in the workout
    var sets: List<ExerciseSet>,     // Sets configuration
    var restTimeBetweenSets: Duration = Duration.ofSeconds(90),
    var restTimeAfterExercise: Duration = Duration.ofSeconds(120),
    var notes: String? = null        // Exercise-specific notes
)

data class ExerciseSet(
    val id: String,
    var setType: SetType,            // Type of set
    var targetReps: Int? = null,     // Target repetitions (null for time-based)
    var targetDuration: Duration? = null, // Target duration (null for rep-based)
    var targetWeight: Double? = null, // Target weight (null for bodyweight)
    var dropSets: List<DropSet>? = null // For drop sets
)

enum class SetType {
    NORMAL,
    WARM_UP,
    DROP_SET,
    FAILURE,
    AMRAP          // As Many Reps As Possible
}

data class DropSet(
    val weight: Double,
    val targetReps: Int
)

Persistence Considerations: - Workouts can exist independently or as part of a program - Efficient retrieval of workouts for current day/week - Support embedded transaction to maintain workout structure integrity

WorkoutSession

WorkoutSession represents a specific instance of a performed workout with recorded performance.

data class WorkoutSession(
    val id: String,                  // Unique identifier
    val workoutId: String,           // Reference to Workout template
    val userId: String,              // User who performed the workout
    val programId: String? = null,   // Program this session is part of (optional)
    var startTime: Instant,          // When the session started
    var endTime: Instant? = null,    // When the session ended (null if ongoing)
    var state: WorkoutState = WorkoutState.IN_PROGRESS,
    var exerciseRecords: List<ExerciseRecord>, // Exercises performed
    var totalVolume: Double? = null, // Total weight lifted
    var notes: String? = null        // Session notes
)

enum class WorkoutState {
    IN_PROGRESS,
    COMPLETED,
    PAUSED,
    ABANDONED
}

data class ExerciseRecord(
    val id: String,
    val exerciseId: String,         // Reference to Exercise
    val workoutExerciseId: String,  // Reference to WorkoutExercise
    var order: Int,                 // Actual order performed (may differ from plan)
    var startTime: Instant,         // When this exercise started
    var endTime: Instant? = null,   // When this exercise ended
    var setRecords: List<SetRecord>, // Sets performed
    var notes: String? = null       // Notes during this exercise
)

data class SetRecord(
    val id: String,
    val setType: SetType,          // Type of set performed
    var repsCompleted: Int? = null, // Actual reps completed
    var weightUsed: Double? = null, // Actual weight used
    var durationCompleted: Duration? = null, // Actual duration
    var difficultyRating: Int? = null, // Perceived difficulty (1-10)
    var completionTime: Instant,   // When this set was logged
    var isPersonalRecord: Boolean = false, // Whether this is a PR
    var notes: String? = null      // Set-specific notes
)

Persistence Considerations: - Room database transaction for saving complete sessions - Support for restoring abandoned/crashed sessions - Efficient query by date range, program, and exercise

Goal

Goal represents a training objective the user wants to achieve.

data class Goal(
    val id: String,                 // Unique identifier
    val userId: String,             // User who set the goal
    var name: String,               // Goal name
    var description: String? = null, // Goal description
    var type: GoalType,             // Type of goal
    var targetValue: Double,        // Target numeric value
    var currentValue: Double,       // Current progress
    var startDate: Instant,         // When the goal started
    var targetDate: Instant? = null, // Deadline (optional)
    var status: GoalStatus = GoalStatus.IN_PROGRESS,
    var exerciseId: String? = null, // For exercise-specific goals
    var programId: String? = null,  // For program-specific goals
    var created: Instant,
    var lastUpdated: Instant
)

enum class GoalType {
    WEIGHT_LIFTED,          // Target weight for an exercise
    FREQUENCY,              // Number of workouts
    VOLUME,                 // Total volume lifted
    BODY_MEASUREMENT,       // Body measurement target
    CONSECUTIVE_DAYS,       // Workout streak
    PROGRAM_COMPLETION      // Complete specific program
}

enum class GoalStatus {
    IN_PROGRESS,
    COMPLETED,
    FAILED,
    ABANDONED
}

Persistence Considerations: - Room database with user-to-goals relationship - Support for automatic progress updates when related activities are recorded - Notification triggers for approaching deadlines and milestones

Achievement

Achievement represents badges and milestones the user has earned.

data class Achievement(
    val id: String,                 // Unique identifier
    val userId: String,             // User who earned the achievement
    val achievementDefinitionId: String, // Reference to achievement definition
    var earnedDate: Instant,        // When the achievement was earned
    var currentProgress: Double? = null, // Progress toward next level (if applicable)
    var level: Int = 1              // Achievement level (for tiered achievements)
)

data class AchievementDefinition(
    val id: String,                 // Unique identifier
    var name: String,               // Achievement name
    var description: String,        // Achievement description
    var category: AchievementCategory, // Achievement category
    var iconUrl: String,            // URL to achievement icon
    var thresholds: List<Double>,   // Thresholds for different levels
    var isHidden: Boolean = false   // Whether this is a secret achievement
)

enum class AchievementCategory {
    CONSISTENCY,
    STRENGTH,
    VOLUME,
    VARIETY,
    MILESTONE,
    SPECIAL
}

Persistence Considerations: - Achievement definitions stored in app assets - Earned achievements stored in Room database - Support for progress tracking and notifications

Relationships Overview

The key relationships between entities are:

  1. UserProfile → EquipmentProfile: One-to-many
    • A user can have multiple equipment profiles for different locations
  2. WorkoutProgram → Workout: One-to-many
    • A program contains multiple workouts organized in a schedule
  3. Workout → WorkoutExercise → Exercise: One-to-many-to-one
    • A workout contains multiple workout exercises
    • Each workout exercise references a specific exercise from the library
  4. WorkoutSession → ExerciseRecord → SetRecord: One-to-many-to-many
    • A workout session contains multiple exercise records
    • Each exercise record contains multiple set records
  5. UserProfile → Goal: One-to-many
    • A user can have multiple goals
  6. Exercise → Exercise: Self-reference
    • Exercises can be variants of other exercises (parent-child relationship)

Persistence Strategy

The application will use a multi-tiered persistence strategy:

1. Room Database (Primary Storage)

  • Store all user-generated data: sessions, custom exercises, custom workouts
  • Store user profile and preferences
  • Support for complex queries and relationships

2. Asset-Based Storage

  • Pre-populated exercise library
  • Default workout programs and templates
  • Achievement definitions

3. Temporary Storage

  • Backup of in-progress sessions for crash recovery (SharedPreferences)
  • Exercise images/videos cache (Disk cache)

4. Remote Backup (Optional)

  • Cloud synchronization of user data with automatic conflict resolution
  • Support for workout sharing via importable codes

Database Schema

Key Tables:

  1. user_profile: User information and preferences
  2. equipment_profiles: Equipment configurations
  3. exercises: Exercise library
  4. workout_programs: Program templates
  5. workouts: Workout templates
  6. workout_exercises: Exercises in workouts with configuration
  7. exercise_sets: Set configurations for workout exercises
  8. workout_sessions: Recorded workout instances
  9. exercise_records: Performed exercises in sessions
  10. set_records: Performed sets in exercise records
  11. goals: User-defined goals
  12. achievements: User-earned achievements

Offline-First Considerations

The application will follow an offline-first approach:

  1. All core functionality will work without an internet connection
  2. Sync conflicts will be resolved using last-modified timestamps
  3. Media content (exercise videos/images) will support progressive loading with cached thumbnails
  4. Background synchronization will occur when connectivity is restored

Data Validation

To ensure data integrity, the following validation rules will be implemented:

  1. Required fields will be enforced at the entity level
  2. Numeric ranges (e.g., difficulty ratings 1-10) will be validated
  3. Relationship integrity will be maintained with foreign keys
  4. Transactions will be used for multi-entity operations

Migration Strategy

Database migrations will follow these principles:

  1. Incremental version-to-version migration paths
  2. Fallback to destructive migration with data export/import for major schema changes
  3. Support for backward compatibility with older data formats
  4. Validation of migrated data integrity

Data Security

To protect user data:

  1. Personal information will be encrypted at rest
  2. Export files will be password-protected
  3. Sensitive measurements will be stored only with explicit user consent
  4. Option to exclude personal data from backups

Kotlin/Native Type Safety

The data model will leverage Kotlin features for type safety:

  1. Sealed classes for representing complex states
  2. Inline classes for value types (e.g., MeasurementValue)
  3. Extension functions for common operations on entities
  4. Immutable collections where appropriate