# Hive CE Database Setup This directory contains the Hive CE (Community Edition) database configuration and services for the Worker Flutter app. ## Overview The app uses Hive CE for offline-first local data persistence. Hive is a lightweight, fast NoSQL database written in pure Dart, perfect for Flutter applications. ## Key Features - **Offline-First**: All data is stored locally and synced with the backend - **Fast Performance**: Hive is optimized for speed with minimal overhead - **Type-Safe**: Uses type adapters for strong typing - **Encryption Support**: Optional AES encryption for sensitive data - **Auto-Compaction**: Automatic database maintenance and cleanup - **Migration Support**: Built-in schema versioning and migrations ## File Structure ``` lib/core/database/ ├── README.md # This file ├── hive_service.dart # Main Hive initialization and lifecycle management ├── database_manager.dart # High-level database operations └── models/ ├── cached_data.dart # Generic cache wrapper model └── enums.dart # All enum type adapters ``` ## Setup Instructions ### 1. Install Dependencies The required packages are already in `pubspec.yaml`: ```yaml dependencies: hive_ce: ^2.6.0 hive_ce_flutter: ^2.1.0 dev_dependencies: hive_ce_generator: ^1.6.0 build_runner: ^2.4.11 ``` Run: ```bash flutter pub get ``` ### 2. Generate Type Adapters After creating Hive models with `@HiveType` annotations, run: ```bash dart run build_runner build --delete-conflicting-outputs ``` Or for continuous watching during development: ```bash dart run build_runner watch --delete-conflicting-outputs ``` ### 3. Initialize Hive in main.dart ```dart import 'package:flutter/material.dart'; import 'core/database/hive_service.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Initialize Hive final hiveService = HiveService(); await hiveService.initialize(); runApp(const MyApp()); } ``` ## Creating Hive Models ### Basic Model Example ```dart import 'package:hive_ce/hive.dart'; import '../../constants/storage_constants.dart'; part 'user_model.g.dart'; // Generated file @HiveType(typeId: HiveTypeIds.user) class UserModel extends HiveObject { @HiveField(0) final String id; @HiveField(1) final String name; @HiveField(2) final String email; UserModel({ required this.id, required this.name, required this.email, }); } ``` ### Enum Example ```dart @HiveType(typeId: HiveTypeIds.memberTier) enum MemberTier { @HiveField(0) gold, @HiveField(1) platinum, @HiveField(2) diamond, } ``` ### Important Rules 1. **Type IDs must be unique** across the entire app (0-223 for user types) 2. **Never change field numbers** once assigned - it will break existing data 3. **Use `part` directive** to include generated adapter file 4. **Extend HiveObject** for model classes (optional but recommended for auto-save) 5. **Register adapters** before opening boxes (handled by HiveService) ## Box Management ### Available Boxes The app uses these pre-configured boxes (see `storage_constants.dart`): - `user_box` - User profile and auth data (encrypted) - `product_box` - Product catalog cache - `cart_box` - Shopping cart items (encrypted) - `order_box` - Order history (encrypted) - `project_box` - Construction projects (encrypted) - `loyalty_box` - Loyalty transactions (encrypted) - `rewards_box` - Rewards catalog - `settings_box` - App settings - `cache_box` - Generic API cache - `sync_state_box` - Sync timestamps - `notification_box` - Notifications - `address_box` - Delivery addresses (encrypted) - `offline_queue_box` - Failed API requests queue (encrypted) ### Using Boxes ```dart // Using DatabaseManager (recommended) final dbManager = DatabaseManager(); // Save data await dbManager.save( boxName: HiveBoxNames.productBox, key: 'product_123', value: product, ); // Get data final product = dbManager.get( boxName: HiveBoxNames.productBox, key: 'product_123', ); // Get all final products = dbManager.getAll(boxName: HiveBoxNames.productBox); ``` ## Caching Strategy ### Save to Cache ```dart final dbManager = DatabaseManager(); await dbManager.saveToCache( key: HiveKeys.productsCacheKey, data: products, ); ``` ### Get from Cache ```dart final products = dbManager.getFromCache>( key: HiveKeys.productsCacheKey, maxAge: CacheDuration.products, // 6 hours ); if (products == null) { // Cache miss or expired - fetch from API final freshProducts = await api.getProducts(); await dbManager.saveToCache( key: HiveKeys.productsCacheKey, data: freshProducts, ); } ``` ### Check Cache Validity ```dart final isValid = dbManager.isCacheValid( key: HiveKeys.productsCacheKey, maxAge: CacheDuration.products, ); if (!isValid) { // Refresh cache } ``` ## Offline Queue Handle failed API requests when offline: ```dart // Add to queue when API call fails await dbManager.addToOfflineQueue({ 'endpoint': '/api/orders', 'method': 'POST', 'body': orderData, }); // Process queue when back online final queue = dbManager.getOfflineQueue(); for (var i = 0; i < queue.length; i++) { try { await api.request(queue[i]); await dbManager.removeFromOfflineQueue(i); } catch (e) { // Keep in queue for next retry } } ``` ## Data Synchronization Track sync state for different data types: ```dart // Update sync timestamp await dbManager.updateSyncTime(HiveKeys.productsSyncTime); // Get last sync time final lastSync = dbManager.getLastSyncTime(HiveKeys.productsSyncTime); // Check if needs sync final needsSync = dbManager.needsSync( dataType: HiveKeys.productsSyncTime, syncInterval: Duration(hours: 6), ); ``` ## Encryption Enable encryption for sensitive data in `storage_constants.dart`: ```dart class HiveDatabaseConfig { static const bool enableEncryption = true; } ``` Generate and store encryption key securely: ```dart // Generate key final encryptionKey = HiveService.generateEncryptionKey(); // Store securely using flutter_secure_storage final secureStorage = FlutterSecureStorage(); await secureStorage.write( key: 'hive_encryption_key', value: base64Encode(encryptionKey), ); // Initialize with key final storedKey = await secureStorage.read(key: 'hive_encryption_key'); await hiveService.initialize( encryptionKey: base64Decode(storedKey!), ); ``` ## Migrations Handle schema changes: ```dart // In hive_service.dart, add migration logic: Future _migrateToVersion(int version) async { switch (version) { case 2: await _migrateV1ToV2(); break; } } Future _migrateV1ToV2() async { // Example: Add new field to existing data final userBox = Hive.box(HiveBoxNames.userBox); for (var key in userBox.keys) { final user = userBox.get(key); // Update user data structure await userBox.put(key, updatedUser); } } ``` ## Database Maintenance ### Clear Expired Cache ```dart await dbManager.clearExpiredCache(); ``` ### Compact Boxes ```dart final hiveService = HiveService(); // Compaction happens automatically during initialization ``` ### Clear User Data (Logout) ```dart await hiveService.clearUserData(); ``` ### Clear All Data ```dart await hiveService.clearAllData(); ``` ### Get Statistics ```dart final stats = dbManager.getStatistics(); dbManager.printStatistics(); ``` ## Best Practices 1. **Always initialize Hive before using any boxes** 2. **Use DatabaseManager for common operations** 3. **Cache frequently accessed data** 4. **Set appropriate cache expiration times** 5. **Handle errors gracefully** - Hive operations can fail 6. **Use transactions for multiple related updates** 7. **Compact boxes periodically** for optimal performance 8. **Never store large files in Hive** - use file system instead 9. **Test migrations thoroughly** before release 10. **Monitor database size** in production ## Debugging ### Print Box Contents ```dart final box = Hive.box(HiveBoxNames.productBox); print('Box length: ${box.length}'); print('Keys: ${box.keys}'); print('Values: ${box.values}'); ``` ### Check Box Location ```dart print('Hive path: ${Hive.box(HiveBoxNames.settingsBox).path}'); ``` ### View Statistics ```dart DatabaseManager().printStatistics(); ``` ## Troubleshooting ### "Box not found" Error - Ensure Hive is initialized before accessing boxes - Check that box name is correct ### "TypeAdapter not registered" Error - Run `build_runner` to generate adapters - Ensure adapter is registered in `HiveService._registerTypeAdapters()` ### "Cannot write null values" Error - Make fields nullable with `?` or provide default values - Check that HiveField annotations are correct ### Data Corruption - Enable backup/restore functionality - Implement data validation before saving - Use try-catch blocks around Hive operations ## Testing ```dart import 'package:flutter_test/flutter_test.dart'; import 'package:hive_ce/hive.dart'; import 'package:hive_ce_flutter/hive_flutter.dart'; void main() { setUp(() async { await Hive.initFlutter(); // Register test adapters }); tearDown(() async { await Hive.deleteFromDisk(); }); test('Save and retrieve user', () async { final box = await Hive.openBox('test_box'); await box.put('user', UserModel(id: '1', name: 'Test')); final user = box.get('user'); expect(user.name, 'Test'); }); } ``` ## Resources - [Hive CE Documentation](https://github.com/IO-Design-Team/hive_ce) - [Original Hive Documentation](https://docs.hivedb.dev/) - [Flutter Offline-First Best Practices](https://flutter.dev/docs/cookbook/persistence) ## Type Adapter Registry ### Registered Type IDs (0-223) | Type ID | Model | Status | |---------|-------|--------| | 0 | UserModel | TODO | | 1 | ProductModel | TODO | | 2 | CartItemModel | TODO | | 3 | OrderModel | TODO | | 4 | ProjectModel | TODO | | 5 | LoyaltyTransactionModel | TODO | | 10 | OrderItemModel | TODO | | 11 | AddressModel | TODO | | 12 | CategoryModel | TODO | | 13 | RewardModel | TODO | | 14 | GiftModel | TODO | | 15 | NotificationModel | TODO | | 16 | QuoteModel | TODO | | 17 | PaymentModel | TODO | | 18 | PromotionModel | TODO | | 19 | ReferralModel | TODO | | 20 | MemberTier (enum) | Created | | 21 | UserType (enum) | Created | | 22 | OrderStatus (enum) | Created | | 23 | ProjectStatus (enum) | Created | | 24 | ProjectType (enum) | Created | | 25 | TransactionType (enum) | Created | | 26 | GiftStatus (enum) | Created | | 27 | PaymentStatus (enum) | Created | | 28 | NotificationType (enum) | Created | | 29 | PaymentMethod (enum) | Created | | 30 | CachedData | Created | | 31 | SyncState | TODO | | 32 | OfflineRequest | TODO | **IMPORTANT**: Never reuse or change these type IDs once assigned!