Back to Home

Baseera Engineering

Server-Authoritative • Offline-First • Production-Ready

3 Engineering Challenges Solved
7 Data Models
500+ Active Users
19 SQLite Schema Versions

System Architecture

A three-tier architecture designed for scalability, security, and offline capability. The backend owns game state, the mobile app provides responsive UI, and PostgreSQL ensures data integrity.

Flutter Mobile

Offline Cache

Provider state management with local SQLite database (baseera.db). Supports offline lesson completion for Pro/Lifetime subscribers with background sync queue.

Laravel Backend

Server Authority

RESTful API with Sanctum authentication. Calculates all XP, streaks, and levels server-side. Eloquent Observers auto-trigger game mechanics on state changes.

PostgreSQL

Source of Truth

Normalized schema with separate assets table for high-frequency updates. Daily XP logs preserve streak integrity during multi-day offline periods.

Laravel 10
PHP 8.1+
Flutter 3.1+
Provider 6.1.4
Sanctum 3.2
Firebase Auth
Heroku

Engineering Challenges

Three interrelated problems that shaped Baseera's technical architecture. Each solution demonstrates production-grade engineering principles.

1

Subscription-Gated Offline Access

The subscription tier controls which features work offline. Free users require internet; paid users cache content locally. This architectural decision manages infrastructure costs while providing premium value.

Free Tier ❌ Offline
Pro/Lifetime ✅ Offline
Dart
// User.dart - Offline eligibility check
class User {
  String subscriptionType;
  
  bool get isPro => subscriptionType.toLowerCase() == 'pro';
  bool get isLifetime => subscriptionType.toLowerCase() == 'lifetime';
  bool get canUseOffline => isPro || isLifetime;
  bool get allowCachePopulate => canUseOffline;
}
24-hour TTL
2-chapter lookahead
3-min sync interval
2

Server-Authoritative Gamification

XP, daily streaks, and level progression are calculated server-side to prevent tampering. The challenge: reconcile multi-day offline activity when paid users eventually reconnect.

Mobile sends raw data
Server calculates XP
Returns canonical state
PHP
// AssetsObserver.php - Streak calculation on XP change
public function updating(Assets $assets): void
{
    if ($assets->isDirty('xp')) {
        $this->updateDaysOfHemma($assets);
        $assets->last_xp_date = now();
    }
}

// Streak logic: strict daily, no grace period
if ($dateDiff == 1) {
    $assets->days_of_hemma += 1;
} else if ($dateDiff > 1) {
    $assets->days_of_hemma = 1; // Streak broken
}
Anti-Cheat
Strict daily streaks
7 progression levels
3

Multi-Day Offline Reconciliation

Pro users can go offline for days. When reconnecting, the app syncs daily XP logs to preserve streak integrity. The backend reconstructs the current streak by counting consecutive days backward.

Multi-day offline
Reconnect
Sync queue
Streak preserved
Dart
// UserProvider.dart - Optimistic completion with retry
Future<void> completeLesson(int lessonId, double score) async {
  // 1. Update UI immediately (optimistic)
  _updateLocalProgress(lessonId, score);
  
  // 2. Add to pending queue
  await _cacheDao.addPendingCompletion(lessonId, score);
  
  // 3. Attempt sync (with exponential backoff)
  await _syncPendingLessons();
}

// Background sync every 3 minutes
Timer.periodic(Duration(minutes: 3), (_) {
  _syncPendingLessons();
});
Auto-retry with cooldown
SQLite pending queue
Daily XP granularity

Data Architecture

A four-level curriculum hierarchy optimized for query performance and content delivery. Separate assets table isolates high-frequency writes from authentication queries.

LearningRoute
name, definition, description
order, is_active
info_sections (JSON)
Chapter
name, order
route_ids (JSON array)
last_lesson_id (auto-calculated)
Lesson
learning_route_id, chapter_id
name, description
in_chapter_order
Question (3 types)
MultipleChoiceQuestion
MatchQuestion
ItqanQuestion (spaced repetition)
Assets (separate table)
xp, feathers (currency)
days_of_hemma (current streak)
last_hemma_date (Y-m-d string, NOT cast to date)
max_days_of_hemma (longest streak)
passed_lessons, passed_exercises

Why separate? Assets update multiple times per day. Storing in the main users table would cause lock contention on authentication queries.

Authentication Architecture

Two-stage authentication: OAuth for identity verification, then Sanctum tokens for API access. Supports Firebase, Google, and Meta OAuth providers.

1

User authenticates via OAuth

User signs in through Firebase, Google, or Meta OAuth. The provider returns an OAuth token containing user identity and email.

2

Mobile app receives OAuth token

The Flutter app captures the OAuth token from the provider's callback and stores it temporarily for verification.

3

Exchange token for Laravel Sanctum token

App sends OAuth token to backend. Laravel validates it with Kreait Firebase SDK, then issues a Sanctum token tied to the user's database record.

4

Sanctum token used for all API requests

The mobile app includes the Sanctum token in the Authorization header for all subsequent API calls. Token persists until logout or expiration.