Task 1 Repositories And Providers
SmartCrew Admin - Task 1 Repositories And Providers
Task 1: Repository Pattern Implementation and Provider Refactoring
Isolation Scope
This task is completely isolated and focuses ONLY on:
- Creating repository layer for data access and caching
- Refactoring network providers to delegate queries to repositories
- Maintaining all existing provider APIs unchanged
Dependencies: None (can be implemented independently) Affected Files: Only providers and new repository files Not Included: Services and adapters (handled in separate tasks)
Goal
Implement the Repository pattern to separate data access logic from business logic while maintaining 100% backward compatibility with existing provider APIs.
Current State - Provider Analysis
NetworkDataProvider Structure
// Current: All data operations mixed in providers
class NetworkDataProvider with UsersProvider, ProjectProvider, EquipmentProvider, etc.
// Each mixin handles:
- Firestore CRUD operations ✅ (Keep)
- Data caching with SplayTreeSet/HashMap ❌ (Move to repositories)
- Complex queries and filtering ❌ (Move to repositories)
- Search and pattern matching ❌ (Move to repositories)Files Currently Using Direct Data Structures
lib/providers/network_data_providers/users_provider.dart- Lines 20-23lib/providers/network_data_providers/project_provider.dart- Lines 14-16lib/providers/network_data_providers/equipment_provider.dart- Lines 14-15lib/providers/network_data_providers/customers_provider.dartlib/providers/network_data_providers/vendors_provider.dartlib/providers/network_data_providers/materials_provider.dartlib/providers/network_data_providers/activity_provider.dart
Implementation Steps
Step 1: Create Base Repository (Isolated)
File: lib/repositories/base_repository.dart
abstract class BaseRepository<T> {
List<T> getAll();
T? getById(String id);
List<T> where(bool Function(T) predicate);
void add(T item);
void remove(T item);
void clear();
String getItemId(T item);
}
abstract class CachedRepository<T> implements BaseRepository<T> {
final SplayTreeSet<T> _items;
final HashMap<String, T> _itemMap;
// Implementation with optimized data structures
}Step 2: Create Domain Repositories (One by One)
2.1 Users Repository
File: lib/repositories/users_repository.dart
Purpose: Handle user data queries and user logs aggregation
class UsersRepository extends CachedRepository<AppUser> {
// User-specific optimized queries
List<AppUser> searchByName(String pattern);
List<AppUser> getActiveUsers();
Map<String, List<DailyLog>> getUserLogs(); // Optimized log access
}2.2 Equipment Repository
File: lib/repositories/equipment_repository.dart
Purpose: Handle equipment queries and service record associations
class EquipmentRepository extends CachedRepository<Equipment> {
List<Equipment> getByType(String type);
List<Equipment> getAvailableEquipment();
}Step 3: Refactor Each Provider (One by One)
3.1 Refactor UsersProvider
Before:
final SplayTreeSet<AppUser> _users = SplayTreeSet(...);
List<AppUser> getUserSuggestions(String pattern) {
return _users.where(...).toList(); // Direct data structure access
}After:
late final UsersRepository _usersRepository;
List<AppUser> getUserSuggestions(String pattern) {
return _usersRepository.searchByName(pattern); // Delegate to repository
}Step 4: Integration with NetworkDataProvider
File: lib/providers/network_data_providers/network_data_provider.dart
Future<void> init(AuthProvider auth) async {
// Initialize repositories before data fetching
_initializeRepositories();
await Future.wait([
fetchUsersFromFireStore(), // Now uses repository for caching
// ... other fetch methods
]);
}
void _initializeRepositories() {
// Initialize all repositories with proper comparators
}Testing & Validation Metrics
Pre-Refactoring Baseline Tests
Run these tests BEFORE starting refactoring:
# 1. Functional Test - Capture current behavior
flutter test test/ --coverage
# Expected: All tests pass (establish baseline)
# 2. Performance Baseline
# Add this test method to capture current performance:
void testCurrentPerformance() {
final stopwatch = Stopwatch()..start();
// Test user search performance
final users = provider.getUserSuggestions("john");
stopwatch.stop();
print('User search time: ${stopwatch.elapsedMilliseconds}ms');
// Test project queries
stopwatch.reset()..start();
final projects = provider.getProjectSuggestions("demo");
stopwatch.stop();
print('Project search time: ${stopwatch.elapsedMilliseconds}ms');
// Memory usage baseline
// Run this in profiler to capture baseline memory
}
# 3. API Contract Test - Document current provider behavior
void testProviderAPIs() {
// Document exact return types and behavior
expect(provider.users, isA<List<AppUser>>());
expect(provider.getUserSuggestions("test"), isA<List<AppUser>>());
expect(provider.getByUserId("id"), isA<AppUser?>());
// ... test all current APIs
}Post-Refactoring Validation Metrics
Metric 1: Zero Breaking Changes
# All existing tests MUST pass without modification
flutter test test/
# SUCCESS CRITERIA: 100% test pass rate (same as baseline)Metric 2: Performance Improvement
# Run performance comparison test
void testPerformanceImprovement() {
// User search should be 30% faster
final userSearchTime = measureUserSearch();
expect(userSearchTime, lessThan(baselineUserSearchTime * 0.7));
// Project queries should be 40% faster
final projectSearchTime = measureProjectSearch();
expect(projectSearchTime, lessThan(baselineProjectSearchTime * 0.6));
// Memory usage should be 20% lower
final memoryUsage = measureMemoryUsage();
expect(memoryUsage, lessThan(baselineMemoryUsage * 0.8));
}Metric 3: API Compatibility
# All provider methods must return identical results
void testAPICompatibility() {
// Before/after comparison for each method
final beforeUsers = captureBeforeResults();
final afterUsers = captureAfterResults();
expect(afterUsers, equals(beforeUsers)); // Exact same data
// Response time should be better or equal
final beforeTime = measureBeforeResponseTime();
final afterTime = measureAfterResponseTime();
expect(afterTime, lessThanOrEqualTo(beforeTime));
}Metric 4: Repository Integration
# Verify repositories are working correctly
void testRepositoryIntegration() {
// Repository should contain same data as before
expect(usersRepository.getAll().length, equals(provider.users.length));
// Repository queries should return correct data
final repoSearch = usersRepository.searchByName("john");
final providerSearch = provider.getUserSuggestions("john");
expect(repoSearch, equals(providerSearch));
// Cache should be properly synchronized
provider.addUser(testUser);
expect(usersRepository.getById(testUser.firebaseUid), equals(testUser));
}Metric 5: Data Consistency
# Verify data integrity maintained
void testDataConsistency() {
// Add user through provider
provider.addUser(testUser);
expect(provider.getByUserId(testUser.firebaseUid), equals(testUser));
expect(usersRepository.getById(testUser.firebaseUid), equals(testUser));
// Update user through provider
provider.updateUser(updatedUser);
expect(provider.getByUserId(testUser.firebaseUid), equals(updatedUser));
expect(usersRepository.getById(testUser.firebaseUid), equals(updatedUser));
// Delete user through provider
provider.removeUser(testUser);
expect(provider.getByUserId(testUser.firebaseUid), isNull);
expect(usersRepository.getById(testUser.firebaseUid), isNull);
}Success Validation Checklist
Functional Validation
- All existing unit tests pass unchanged
- All provider APIs return identical results
- No UI behavior changes
- App startup works correctly
- Data loading works correctly
Performance Validation
- User search queries are 30% faster
- Project queries are 40% faster
- Memory usage reduced by 20%
- No performance regression in any operation
- App initialization time improved by 15%
Integration Validation
- Repositories properly initialized
- Provider-repository data sync works
- Concurrent access handled correctly
- Memory leaks prevented
- Error handling maintained
Compatibility Validation
- No changes to provider public APIs
- All existing method signatures unchanged
- Return types identical to before
- Error cases handled identically
- Null handling behavior preserved
Rollback Strategy
If any metric fails:
- Immediate Rollback: Revert to direct data structure usage in providers
- Feature Flag: Implement repository toggle for gradual testing
- Partial Rollback: Rollback individual providers independently
- Data Validation: Ensure no data corruption during rollback
Files Modified (Complete List)
New Files Created
lib/repositories/base_repository.dartlib/repositories/users_repository.dartlib/repositories/project_repository.dartlib/repositories/equipment_repository.dartlib/repositories/materials_repository.dartlib/repositories/customers_repository.dartlib/repositories/vendors_repository.dartlib/repositories/activity_repository.dart
Existing Files Modified
lib/providers/network_data_providers/network_data_provider.dartlib/providers/network_data_providers/users_provider.dartlib/providers/network_data_providers/project_provider.dartlib/providers/network_data_providers/equipment_provider.dartlib/providers/network_data_providers/materials_provider.dartlib/providers/network_data_providers/customers_provider.dartlib/providers/network_data_providers/vendors_provider.dartlib/providers/network_data_providers/activity_provider.dart
Total Files: 16 (8 new, 8 modified) Scope: Only providers and repositories - no services or adapters touched