Files
worker/lib/core/database
2025-11-03 17:05:47 +07:00
..
2025-10-24 11:31:48 +07:00
2025-10-17 17:22:28 +07:00
2025-11-03 15:02:33 +07:00
2025-10-17 17:22:28 +07:00
2025-11-03 17:05:47 +07:00
2025-10-17 17:22:28 +07:00
2025-10-17 17:22:28 +07:00

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:

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:

flutter pub get

2. Generate Type Adapters

After creating Hive models with @HiveType annotations, run:

dart run build_runner build --delete-conflicting-outputs

Or for continuous watching during development:

dart run build_runner watch --delete-conflicting-outputs

3. Initialize Hive in main.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

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

@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

// 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

final dbManager = DatabaseManager();

await dbManager.saveToCache(
  key: HiveKeys.productsCacheKey,
  data: products,
);

Get from Cache

final products = dbManager.getFromCache<List<Product>>(
  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

final isValid = dbManager.isCacheValid(
  key: HiveKeys.productsCacheKey,
  maxAge: CacheDuration.products,
);

if (!isValid) {
  // Refresh cache
}

Offline Queue

Handle failed API requests when offline:

// 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:

// 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:

class HiveDatabaseConfig {
  static const bool enableEncryption = true;
}

Generate and store encryption key securely:

// 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:

// In hive_service.dart, add migration logic:

Future<void> _migrateToVersion(int version) async {
  switch (version) {
    case 2:
      await _migrateV1ToV2();
      break;
  }
}

Future<void> _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

await dbManager.clearExpiredCache();

Compact Boxes

final hiveService = HiveService();
// Compaction happens automatically during initialization

Clear User Data (Logout)

await hiveService.clearUserData();

Clear All Data

await hiveService.clearAllData();

Get Statistics

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

final box = Hive.box(HiveBoxNames.productBox);
print('Box length: ${box.length}');
print('Keys: ${box.keys}');
print('Values: ${box.values}');

Check Box Location

print('Hive path: ${Hive.box(HiveBoxNames.settingsBox).path}');

View Statistics

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

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

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!