Skip to content

Data Models

Garot Conklin edited this page Dec 17, 2024 · 2 revisions

Data Models

Overview

This document details the data models used throughout the RunOn! application, including database schemas, API DTOs, and domain models.

Domain Models

1. Event

data class Event(
    val id: String,
    val title: String,
    val description: String,
    val date: LocalDateTime,
    val location: Location,
    val distance: Distance,
    val eventType: EventType,
    val organizer: Organizer,
    val price: Price?,
    val registrationDeadline: LocalDateTime?,
    val participantLimit: Int?,
    val currentParticipants: Int,
    val status: EventStatus,
    val tags: List<String>,
    val imageUrl: String?,
    val websiteUrl: String?,
    val createdAt: Instant,
    val updatedAt: Instant
)

data class Location(
    val name: String,
    val address: String,
    val city: String,
    val state: String,
    val country: String,
    val postalCode: String,
    val coordinates: Coordinates
)

data class Coordinates(
    val latitude: Double,
    val longitude: Double
)

data class Distance(
    val value: Double,
    val unit: DistanceUnit
)

enum class DistanceUnit {
    KILOMETERS,
    MILES
}

enum class EventType {
    RACE,
    FUN_RUN,
    TRAINING,
    CHARITY,
    ULTRA,
    TRAIL,
    TRACK
}

enum class EventStatus {
    UPCOMING,
    ONGOING,
    COMPLETED,
    CANCELLED,
    SOLD_OUT
}

2. User

data class User(
    val id: String,
    val email: String,
    val profile: UserProfile,
    val preferences: UserPreferences,
    val stats: UserStats,
    val createdAt: Instant,
    val updatedAt: Instant
)

data class UserProfile(
    val displayName: String,
    val firstName: String?,
    val lastName: String?,
    val bio: String?,
    val avatarUrl: String?,
    val location: Location?
)

data class UserPreferences(
    val preferredDistances: List<Distance>,
    val preferredEventTypes: List<EventType>,
    val maxTravelDistance: Distance,
    val preferredLocation: Location?,
    val notificationSettings: NotificationSettings
)

data class UserStats(
    val eventsParticipated: Int,
    val totalDistance: Distance,
    val achievements: List<Achievement>
)

Database Schema (MongoDB)

User Collection

{
  "_id": "ObjectId",
  "auth0Id": "string",
  "email": "string",
  "profile": {
    "name": "string",
    "location": {
      "type": "Point",
      "coordinates": [longitude, latitude]
    },
    "preferences": {
      "searchRadius": "number",
      "eventTypes": ["string"],
      "notifications": "boolean"
    }
  },
  "calendar": {
    "googleCalendarId": "string?",
    "syncEnabled": "boolean"
  },
  "createdAt": "ISODate",
  "updatedAt": "ISODate"
}

Events Collection

{
  "_id": "ObjectId",
  "sourceId": "string",
  "source": "enum('google', 'manual', 'partner')",
  "title": "string",
  "description": "string",
  "location": {
    "type": "Point",
    "coordinates": [longitude, latitude],
    "address": "string"
  },
  "date": {
    "start": "ISODate",
    "end": "ISODate"
  },
  "type": "string",
  "url": "string",
  "metadata": {
    "distance": "string?",
    "terrain": "string?",
    "difficulty": "string?",
    "price": "number?"
  },
  "createdAt": "ISODate",
  "updatedAt": "ISODate"
}

UserEvents Collection

{
  "_id": "ObjectId",
  "userId": "ObjectId",
  "eventId": "ObjectId",
  "status": "enum('interested', 'registered', 'completed')",
  "calendarSync": "boolean",
  "notes": "string?",
  "createdAt": "ISODate",
  "updatedAt": "ISODate"
}

Cache Structure (Redis)

Event Cache

// Key: event:{eventId}
// TTL: 15 minutes
interface EventCache {
  id: string;
  title: string;
  date: ISOString;
  location: {
    lat: number;
    lng: number;
    address: string;
  };
}

Search Results Cache

// Key: search:{location}:{radius}:{types}
// TTL: 5 minutes
interface SearchCache {
  events: string[]; // Array of event IDs
  timestamp: ISOString;
}

User Preferences Cache

// Key: user:{userId}:preferences
// TTL: 1 hour
interface PreferencesCache {
  location: {
    lat: number;
    lng: number;
    radius: number;
  };
  eventTypes: string[];
}

API Response Models

Event Response

interface EventResponse {
  id: string;
  title: string;
  description: string;
  date: {
    start: ISOString;
    end: ISOString;
  };
  location: {
    lat: number;
    lng: number;
    address: string;
  };
  type: string;
  url: string;
  metadata?: {
    distance?: string;
    terrain?: string;
    difficulty?: string;
    price?: number;
  };
}

Search Response

interface SearchResponse {
  events: EventResponse[];
  pagination: {
    total: number;
    page: number;
    pageSize: number;
    hasMore: boolean;
  };
  metadata: {
    location: {
      lat: number;
      lng: number;
      radius: number;
    };
    filters: {
      types: string[];
      dateRange?: {
        start: ISOString;
        end: ISOString;
      };
    };
  };
}

Indexes

MongoDB Indexes

// Events Collection
db.events.createIndex({ "location": "2dsphere" });
db.events.createIndex({ "date.start": 1 });
db.events.createIndex({ "sourceId": 1 }, { unique: true });

// Users Collection
db.users.createIndex({ "auth0Id": 1 }, { unique: true });
db.users.createIndex({ "profile.location": "2dsphere" });

// UserEvents Collection
db.userEvents.createIndex({ "userId": 1, "eventId": 1 }, { unique: true });
db.userEvents.createIndex({ "userId": 1, "status": 1 });

Data Validation

MongoDB Validation Rules

{
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["title", "date", "location"],
      properties: {
        title: {
          bsonType: "string",
          description: "must be a string and is required"
        },
        location: {
          bsonType: "object",
          required: ["type", "coordinates"],
          properties: {
            type: { enum: ["Point"] },
            coordinates: {
              bsonType: ["array"],
              minItems: 2,
              maxItems: 2,
              items: { bsonType: "double" }
            }
          }
        }
      }
    }
  }
}

Data Migration Strategies

Version Control

{
  "_id": "ObjectId",
  "version": "number",
  "appliedAt": "ISODate",
  "description": "string"
}

Backup Strategy

  • Daily automated backups
  • Point-in-time recovery enabled
  • 7-day retention period
  • Cross-region backup for disaster recovery

RunOn Documentation

MVP Documentation

Core Documentation

Archived (Full-Featured)

Full-Featured Documentation

Clone this wiki locally