Data Models
Database schema
The Primary device runs a Drift (SQLite) database at schema version 66 with 67 tables. The schema is defined in:
lib/core/database/app_database.dart(53 KB) —AppDatabaseclass extending generated_$AppDatabaselib/core/database/tables.dart(51 KB) — all Drift table definitionslib/core/database/app_database.g.dart(2.3 MB) — Drift-generated DAO code
Schema design principles
- Guarded migrations:
_hasColumn(),_hasTable(),_addColumnIfMissing()make upgrades safe to resume after partial failure and safe for multi-version jumps - Denormalized counters:
Customerstable carriestotalSpend,visitCount,pointsBalancefor instant dashboard reads - Device-aware rows: Every table carries
deviceId,version, andsyncedToCloudcolumns for multi-device conflict resolution - Financial snapshots:
Ticketsrows writesubtotal,tax,surcharge,tip,total,discountAmountonce at checkout — they are immutable after close
Major table groups
Cloud sync outbox
The sync outbox is a write-ahead log that tracks every mutation for cloud upload.
Tables
sync_outbox: One row per mutation (upsertordelete), withtable_name,pk_value,operation,sequencesync_buffer: EmergencyBuffer queue for Secondary offline writes
Upload pipeline
Key source files: lib/core/sync/cloud_sync_worker.dart, cloud_sync_collector.dart, cloud_sync_client.dart, cloud_sync_scheduler.dart
Sync table registry
lib/core/sync/sync_table_registry.dart declares which tables sync to the cloud:
- 47 tables are syncable (in
kSyncTableRegistry) - 6 tables excluded:
sync_outbox,sync_buffer,app_settings,idempotency_keys,booking_requests,app_notifications - Each
SyncTableSpecdeclares: table name, PK column, excluded columns, redacted columns - An anti-drift test ensures every
AppDatabasetable is either in the registry or the exclusion set
Coalescing
The collector performs outbox coalescing: for multiple mutations to the same (table, pk), only the highest-sequence entry is kept.
Conflict handling
- 409 Conflict: Cloud signals a data conflict — the row is skipped for that tick
- 401 Unauthorized: Lease lost — triggers demotion
- 403 Forbidden: Store pending activation
- Network errors: Exponential backoff with jitter
Restore pipeline
Domain-aware cache invalidation
lib/core/data/data_domain.dart and domain_dispatch.dart:
DataDomainenum: 14 domains —tickets,appointments,staff,staffSchedule,catalogue,membership,resources,reporting,waitlist,giftCards,sessions,bookingRequests,packagesdomainProviders(domain): Maps each domain to its feature-owned Riverpod provider listinvalidateDomains(ref, domains): Invalidates all providers for the given domains- Pre-built bundles:
dashboardDomains(4 domains),checkoutDomains(8 domains)
Shared domain models
lib/shared/models/:
Demo seed data
lib/core/database/demo_seed.dart (81 KB) populates the database with sample data for development and testing. Called during bootstrap via seedAllDemoData().