Skip to main content

TekTree Data Models

Version: 1.0.0 Last Updated: 2025-12-16 Status: Foundation (Pre-Implementation)

MongoDB Collections

users

Purpose: User accounts and profiles
{
  "_id": ObjectId("..."),
  "uid": "usr_abc123",  // External ID
  "email": "user@example.com",
  "email_verified": false,
  "password_hash": "bcrypt...",
  "handle": "johndoe",
  "first_name": "John",
  "last_name": "Doe",
  "avatar": "https://cdn.tektree.com/avatars/...",
  "bio": "Software engineer...",
  "tier": "pro",
  "role": "user",  // user, moderator, admin
  "created_at": ISODate("2025-01-01T00:00:00Z"),
  "updated_at": ISODate("2025-12-16T14:00:00Z"),
  "last_login_at": ISODate("2025-12-16T14:00:00Z")
}

// Indexes
db.users.createIndex({ "email": 1 }, { unique: true })
db.users.createIndex({ "handle": 1 }, { unique: true })
db.users.createIndex({ "uid": 1 }, { unique: true })
db.users.createIndex({ "created_at": -1 })

areas

Purpose: Knowledge organization hierarchy
{
  "_id": ObjectId("..."),
  "title": "Event-Driven Architecture",
  "slug": "event-driven-architecture",
  "parent_id": ObjectId("..."),  // null for top-level
  "details": "Exploring event-driven patterns...",
  "tags": ["architecture", "events"],
  "breadcrumbs": "Programming > Architecture > Event-Driven",
  "visibility": "public",  // public, private, connections
  "created_by": ObjectId("..."),  // user _id
  "created_at": ISODate("..."),
  "updated_at": ISODate("..."),
  "follower_count": 234,
  "content_count": 45
}

// Indexes
db.areas.createIndex({ "parent_id": 1, "created_at": -1 })
db.areas.createIndex({ "tags": 1 })
db.areas.createIndex({ "created_by": 1 })
db.areas.createIndex({ "slug": 1 })
db.areas.createIndex({ "title": "text", "details": "text" })

questions

Purpose: User questions
{
  "_id": ObjectId("..."),
  "title": "How do I implement event sourcing?",
  "slug": "how-do-i-implement-event-sourcing",
  "body": "I'm building an event-driven system...",
  "tags": ["go", "event-sourcing"],
  "areas": [ObjectId("..."), ObjectId("...")],
  "visibility": "public",
  "created_by": ObjectId("..."),
  "created_at": ISODate("..."),
  "updated_at": ISODate("..."),
  "view_count": 234,
  "upvote_count": 12,
  "downvote_count": 1,
  "answer_count": 3,
  "accepted_answer_id": ObjectId("..."),
  "files": ["file_abc123"],
  "status": "open"  // open, closed, resolved
}

// Indexes
db.questions.createIndex({ "created_by": 1, "created_at": -1 })
db.questions.createIndex({ "areas": 1, "created_at": -1 })
db.questions.createIndex({ "tags": 1 })
db.questions.createIndex({ "status": 1, "created_at": -1 })
db.questions.createIndex({ "title": "text", "body": "text" })

answers

Purpose: Answers to questions
{
  "_id": ObjectId("..."),
  "question_id": ObjectId("..."),
  "body": "Here's how you can implement...",
  "code_snippets": [
    {
      "language": "go",
      "code": "type Event struct {...}"
    }
  ],
  "created_by": ObjectId("..."),
  "created_at": ISODate("..."),
  "updated_at": ISODate("..."),
  "upvote_count": 8,
  "downvote_count": 0,
  "is_accepted": true
}

// Indexes
db.answers.createIndex({ "question_id": 1, "created_at": -1 })
db.answers.createIndex({ "created_by": 1 })
db.answers.createIndex({ "is_accepted": 1 })

insights

Purpose: Long-form articles and tutorials
{
  "_id": ObjectId("..."),
  "title": "Event-Driven Architecture Best Practices",
  "slug": "event-driven-architecture-best-practices",
  "body": "# Introduction\n\n...",
  "insight_type": "article",  // article, tutorial, case_study, tool_review
  "tags": ["architecture", "events"],
  "areas": [ObjectId("...")],
  "co_authors": [ObjectId("...")],
  "visibility": "public",
  "created_by": ObjectId("..."),
  "created_at": ISODate("..."),
  "updated_at": ISODate("..."),
  "published_at": ISODate("..."),
  "view_count": 1234,
  "like_count": 56,
  "comment_count": 12,
  "estimated_read_time_minutes": 10,
  "featured": false
}

// Indexes
db.insights.createIndex({ "created_by": 1, "published_at": -1 })
db.insights.createIndex({ "areas": 1, "published_at": -1 })
db.insights.createIndex({ "tags": 1 })
db.insights.createIndex({ "featured": 1, "published_at": -1 })
db.insights.createIndex({ "title": "text", "body": "text" })

gamification_profiles

Purpose: User gamification state
{
  "_id": ObjectId("..."),
  "user_id": ObjectId("..."),
  "total_xp": 50000,
  "current_level": 15,
  "xp_to_next_level": 1500,
  "daily_xp_earned": 150,
  "daily_xp_reset_at": ISODate("2025-12-17T00:00:00Z"),
  "streak": {
    "current": 42,
    "longest": 87,
    "last_activity_at": ISODate("2025-12-16T20:00:00Z"),
    "freezes_available": 6
  },
  "achievements": ["ach_first_steps", "ach_week_warrior"],
  "featured_achievements": ["ach_week_warrior", "ach_top_contributor"],
  "leaderboard_opt_out": false,
  "updated_at": ISODate("...")
}

// Indexes
db.gamification_profiles.createIndex({ "user_id": 1 }, { unique: true })
db.gamification_profiles.createIndex({ "total_xp": -1 })  // Global leaderboard
db.gamification_profiles.createIndex({ "current_level": -1 })

xp_transactions

Purpose: XP earning history (event sourcing)
{
  "_id": ObjectId("..."),
  "transaction_id": "xp_txn_abc123",
  "user_id": ObjectId("..."),
  "amount": 25,
  "source": "insight_published",
  "related_entity_type": "insight",
  "related_entity_id": ObjectId("..."),
  "daily_total": 75,
  "daily_cap": 500,
  "timestamp": ISODate("...")
}

// Indexes
db.xp_transactions.createIndex({ "user_id": 1, "timestamp": -1 })
db.xp_transactions.createIndex({ "transaction_id": 1 }, { unique: true })
db.xp_transactions.createIndex({ "source": 1 })

subscriptions

Purpose: User subscription and billing
{
  "_id": ObjectId("..."),
  "user_id": ObjectId("..."),
  "tier": "pro",  // free, pro, team, enterprise
  "status": "active",  // active, trialing, past_due, canceled
  "billing_cycle": "monthly",  // monthly, annual
  "polar_subscription_id": "sub_polar_xyz",
  "current_period_start": ISODate("2025-12-01T00:00:00Z"),
  "current_period_end": ISODate("2025-12-31T23:59:59Z"),
  "trial_ends_at": null,
  "cancel_at": null,
  "canceled_at": null,
  "created_at": ISODate("..."),
  "updated_at": ISODate("...")
}

// Indexes
db.subscriptions.createIndex({ "user_id": 1 }, { unique: true })
db.subscriptions.createIndex({ "status": 1 })
db.subscriptions.createIndex({ "polar_subscription_id": 1 })

events

Purpose: Event sourcing log
{
  "_id": ObjectId("..."),
  "event_id": "evt_abc123xyz",
  "event_type": "user.registered",
  "event_version": "1.0.0",
  "timestamp": ISODate("..."),
  "trace_id": "trace_abc123",
  "correlation_id": "corr_xyz789",
  "source_service": "user-service",
  "producer_id": "user-svc-pod-1",
  "metadata": {
    "user_id": "usr_123",
    "ip_address": "203.0.113.42"
  },
  "payload": {
    "user_id": "usr_abc123",
    "email": "user@example.com"
  }
}

// Indexes
db.events.createIndex({ "event_id": 1 }, { unique: true })
db.events.createIndex({ "event_type": 1, "timestamp": -1 })
db.events.createIndex({ "trace_id": 1 })
db.events.createIndex({ "timestamp": -1 })
db.events.createIndex({ "payload.user_id": 1, "timestamp": -1 })

Redis Data Structures

Session Storage

session:{session_id} -> JSON
TTL: 7 days (or 30 days with remember_me)

{
  "user_id": "usr_abc123",
  "tier": "pro",
  "role": "user",
  "created_at": 1702742400,
  "expires_at": 1703347200
}

Rate Limiting

ratelimit:{user_id}:{endpoint} -> count
TTL: 60 seconds (sliding window)

ratelimit:usr_abc123:/api/v1/questions -> 5

Quota Tracking

quota:{user_id}:{quota_type}:{period} -> count
TTL: End of billing period

quota:usr_abc123:questions:2025-12 -> 15

Cache

cache:user:{user_id} -> JSON
TTL: 5 minutes

cache:question:{question_id} -> JSON
TTL: 5 minutes

Leaderboard (Sorted Sets)

leaderboard:global -> ZADD user_id score
leaderboard:weekly:2025-W50 -> ZADD user_id score

Document Status: ✅ Complete Related Documents: ARCHITECTURE_OVERVIEW.md, EVENT_CATALOG.md, API_CONTRACTS.md