Task 2 Services Optimization
SmartCrew Admin - Task 2 Services Optimization
Task 2: Services Layer Optimization and Consolidation
Isolation Scope
This task is completely isolated and focuses ONLY on:
- Optimizing existing service classes for performance
- Consolidating duplicate service logic
- Creating base service classes for common patterns
- NO changes to providers, repositories, or adapters
Dependencies: None (completely independent of Task 1 and Task 3)
Affected Files: Only files in lib/services/ directory (45 service files)
Not Included: Providers, repositories, adapters, or UI components
Goal
Optimize service layer performance by 40% and reduce code duplication by 50% while maintaining identical public APIs and calculation results.
Current State - Service Analysis
Service Categories (45 files total)
Labor Services (13 files in lib/services/labor/)
Performance Issues Identified:
labor_total_time_service.dart- Multiple iterations over same datasetlabor_onsite_log_service.dart- Lines 14-32: Nearly identical to traveling servicelabor_traveling_log_service.dart- Lines 14-36: 90% duplicate of onsite servicelabor_machine_time_service.dart- Inefficient task filtering- Time card services (7 files) - Repetitive calculation patterns
Equipment Services (5 files in lib/services/equipment/)
Performance Issues:
- Multiple services iterate over equipment logs separately
- No caching of expensive calculations
- Redundant filtering operations
Log Services (8 files in lib/services/log/)
Duplicate Patterns:
- Similar log calculation logic across multiple services
- Repeated date filtering operations
- Common duration calculation patterns
Implementation Steps
Step 1: Create Base Service Classes (Isolated)
1.1 Base Log Calculation Service
File: lib/services/base/base_log_calculation_service.dart
abstract class BaseLogCalculationService {
final DateTime? date;
final Iterable<DailyLog> allUserLogs;
const BaseLogCalculationService({required this.date, required this.allUserLogs});
// Common filtering logic (used by 8+ services)
Iterable<DailyLog> getFilteredLogs() {
return allUserLogs.where((log) =>
!log.isDayOff &&
log.clockIn != null &&
log.isClockedOut &&
log.status != RequestStatus.denied &&
_isInRange(log)
);
}
// Optimized single-pass task extraction
Iterable<Task> extractTasksWithFilter(bool Function(WorkActivity) activityFilter);
bool _isInRange(DailyLog element) => date == null || element.logDate.isSameDate(date!);
}1.2 Base Time Calculation Service
File: lib/services/base/base_time_calculation_service.dart
abstract class BaseTimeCalculationService extends BaseLogCalculationService {
const BaseTimeCalculationService({required super.date, required super.allUserLogs});
// Optimized duration calculation with memoization
Duration calculateDuration() {
final tasks = getFilteredTasks();
return TaskDurationCalculator.calculateOverlapAwareDuration(tasks);
}
// Abstract method for different filtering strategies
Iterable<Task> getFilteredTasks();
}Step 2: Consolidate Duplicate Services
2.1 Merge Labor Location Services
Current Duplicate Code:
LaborOnsiteLogService(36 lines)LaborTravelingLogService(37 lines)- 90% identical logic, only filter criteria differs
New Consolidated Service:
File: lib/services/labor/labor_log_filter_service.dart
enum LogFilter {
onsite, // jobsiteId != travelingId && jobsiteId.isNotEmpty
traveling, // jobsiteId == travelingId || jobsiteId.isEmpty
machine, // has machine activities
all // no filtering
}
class LaborLogFilterService extends BaseTimeCalculationService {
final LogFilter filter;
const LaborLogFilterService({
required this.filter,
required super.date,
required super.allUserLogs
});
@override
Iterable<Task> getFilteredTasks() {
final workLogs = getFilteredLogs().expand((log) => log.jobsiteActivities);
return workLogs
.where(_getActivityFilter())
.expand((activity) => activity.tasks)
.where((task) => task.status == TaskStatus.approved);
}
bool Function(WorkActivity) _getActivityFilter() {
switch (filter) {
case LogFilter.onsite:
return (activity) => activity.jobsiteId != JobSite.travelingJobsiteId &&
activity.jobsiteId.isNotEmpty;
case LogFilter.traveling:
return (activity) => activity.jobsiteId == JobSite.travelingJobsiteId ||
activity.jobsiteId.isEmpty;
case LogFilter.machine:
return (activity) => activity.hasMachineActivities;
case LogFilter.all:
return (activity) => true;
}
}
}2.2 Update Existing Services to Use Consolidated Version
Replace: LaborOnsiteLogService
// OLD - Delete this file
class LaborOnsiteLogService {
Duration calculate() {
// 32 lines of duplicate code
}
}
// NEW - Use consolidated service
class LaborOnsiteLogService {
final DateTime? date;
final Iterable<DailyLog> allUserLogs;
const LaborOnsiteLogService({required this.date, required this.allUserLogs});
Duration calculate() {
return LaborLogFilterService(
filter: LogFilter.onsite,
date: date,
allUserLogs: allUserLogs
).calculateDuration();
}
}Step 3: Optimize Performance-Critical Services
3.1 Optimize Labor Daily Log Service
Current Issues in labor_daily_log_service.dart:
- Lines 20-41: Multiple service instantiations for same dataset
- No caching of intermediate results
- Repeated iteration over same logs
Optimization:
class LaborDailyLogService {
final AppUser user;
final DateTime date;
final NetworkDataProvider provider = NetworkDataProvider();
// Cache for expensive calculations
static final Map<String, Duration> _calculationCache = {};
Labor? createLaborInfo() {
final userLogs = provider.logs
.firstWhereOrNull((userLog) => userLog.user.firebaseUid == user.firebaseUid)?.logs;
if (userLogs == null || userLogs.isEmpty) return null;
// Single-pass calculation instead of 4 separate service calls
final calculations = _calculateAllDurations(userLogs, date);
if (calculations.totalTime.inMinutes == 0) return null;
return Labor(
firebaseUid: user.firebaseUid,
phoneNumber: user.phoneNumber,
employee: user.fullName,
role: user.role,
status: user.active,
totalHours: calculations.totalTime.formatHourMinute,
shopHours: calculations.shopTime.formatHourMinute,
travelHours: calculations.travelTime.formatHourMinute,
machineHours: calculations.machineTime.formatHourMinute
);
}
// Optimized single-pass calculation
LaborCalculations _calculateAllDurations(List<DailyLog> logs, DateTime date) {
final cacheKey = '${user.firebaseUid}_${date.millisecondsSinceEpoch}';
// Check cache first
if (_calculationCache.containsKey(cacheKey)) {
return _getCachedCalculations(cacheKey);
}
// Single service instance handles all calculations
final calculator = OptimizedLaborCalculatorService(logs: logs, date: date);
final results = calculator.calculateAll();
// Cache results
_cacheCalculations(cacheKey, results);
return results;
}
}Step 4: Create Service Factory Pattern
4.1 Time Card Service Factory
Current: 7 separate time card service files with similar constructors Optimized: Single factory with type-safe creation
File: lib/services/labor/time_card/labor_time_card_factory.dart
enum TimeCardType { daily, weekly, monthly, yearly, all }
class LaborTimeCardFactory {
static LaborTimeCardService create({
required TimeCardType type,
required AppUser user,
required DateTime date,
}) {
switch (type) {
case TimeCardType.daily:
return LaborDailyTimeCardService(user: user, date: date);
case TimeCardType.weekly:
return LaborWeekTimeCardService(user: user, startDate: date);
case TimeCardType.monthly:
return LaborMonthTimeCardService(user: user, month: date);
// ... other types
}
}
// Batch creation for performance
static Map<TimeCardType, LaborTimeCardService> createAll({
required AppUser user,
required DateTime date,
}) {
return {
for (final type in TimeCardType.values)
type: create(type: type, user: user, date: date)
};
}
}Testing & Validation Metrics
Pre-Optimization Baseline Tests
# 1. Capture Current Performance Baseline
void captureServicePerformance() {
final stopwatch = Stopwatch()..start();
// Labor calculation baseline
final laborService = LaborDailyLogService(user: testUser, date: testDate);
final labor = laborService.createLaborInfo();
stopwatch.stop();
print('Labor calculation baseline: ${stopwatch.elapsedMilliseconds}ms');
// Equipment calculation baseline
stopwatch.reset()..start();
final equipmentService = EquipmentLogsService(equipment: testEquipment);
final logs = equipmentService.getUsageLogs();
stopwatch.stop();
print('Equipment calculation baseline: ${stopwatch.elapsedMilliseconds}ms');
}
# 2. Capture Exact Calculation Results
void captureCalculationBaseline() {
// Document exact calculation results for comparison
final results = {};
results['totalTime'] = laborService.calculateTotalTime();
results['onsiteTime'] = onsiteService.calculate();
results['travelTime'] = travelService.calculate();
// Store all current calculation results
}
# 3. Memory Usage Baseline
void captureMemoryBaseline() {
// Run service operations and measure memory usage
final memoryBefore = getMemoryUsage();
runAllServiceOperations();
final memoryAfter = getMemoryUsage();
final baseline = memoryAfter - memoryBefore;
print('Service memory baseline: ${baseline}MB');
}Post-Optimization Validation Metrics
Metric 1: Identical Calculation Results
# All calculations must return EXACTLY the same results
void testCalculationAccuracy() {
final beforeResults = runBaselineCalculations();
final afterResults = runOptimizedCalculations();
expect(afterResults.totalTime, equals(beforeResults.totalTime));
expect(afterResults.onsiteTime, equals(beforeResults.onsiteTime));
expect(afterResults.travelTime, equals(beforeResults.travelTime));
expect(afterResults.machineTime, equals(beforeResults.machineTime));
// Test with edge cases
expect(calculateWithEmptyLogs(), equals(Duration.zero));
expect(calculateWithNullDates(), equals(baselineNullDateResult));
}Metric 2: Performance Improvement (40% target)
void testPerformanceImprovement() {
// Labor calculation should be 40% faster
final laborTime = measureLaborCalculation();
expect(laborTime, lessThan(baselineLaborTime * 0.6));
// Equipment services should be 35% faster
final equipmentTime = measureEquipmentCalculation();
expect(equipmentTime, lessThan(baselineEquipmentTime * 0.65));
// Time card generation should be 50% faster
final timeCardTime = measureTimeCardGeneration();
expect(timeCardTime, lessThan(baselineTimeCardTime * 0.5));
// Overall service layer should be 40% faster
final overallTime = measureAllServiceOperations();
expect(overallTime, lessThan(baselineOverallTime * 0.6));
}Metric 3: Memory Usage Reduction (30% target)
void testMemoryOptimization() {
final memoryUsage = measureServiceMemoryUsage();
expect(memoryUsage, lessThan(baselineMemoryUsage * 0.7));
// No memory leaks in repetitive operations
for (int i = 0; i < 1000; i++) {
runServiceOperation();
}
final finalMemory = getMemoryUsage();
expect(finalMemory, lessThan(baselineMemoryUsage * 1.1)); // Max 10% increase
}Metric 4: Code Duplication Reduction (50% target)
void testCodeDuplication() {
// Count lines of duplicate code before/after
final beforeDuplicateLines = countDuplicateServiceLines();
final afterDuplicateLines = countDuplicateServiceLines();
final reduction = (beforeDuplicateLines - afterDuplicateLines) / beforeDuplicateLines;
expect(reduction, greaterThan(0.5)); // 50% reduction target
// Verify base classes are being used
expect(laborOnsiteService, isA<BaseTimeCalculationService>());
expect(laborTravelingService, isA<BaseTimeCalculationService>());
}Metric 5: Service API Compatibility
void testServiceAPICompatibility() {
// All public service methods must work unchanged
expect(() => LaborDailyLogService(user: user, date: date), returnsNormally);
expect(() => laborService.createLaborInfo(), returnsNormally);
// Return types must be identical
final labor = laborService.createLaborInfo();
expect(labor, isA<Labor?>());
expect(labor?.totalHours, isA<String>());
// Constructor signatures must be unchanged
expect(() => LaborTotalTimeService(date: date, allUserLogs: logs), returnsNormally);
expect(() => EquipmentLogsService(equipment: equipment), returnsNormally);
}Metric 6: Edge Case Handling
void testEdgeCaseHandling() {
// Empty data handling
final emptyResult = serviceWithEmptyData.calculate();
expect(emptyResult, equals(Duration.zero));
// Null data handling
final nullResult = serviceWithNullData.calculate();
expect(nullResult, equals(baselineNullResult));
// Invalid date handling
final invalidDateResult = serviceWithInvalidDate.calculate();
expect(invalidDateResult, equals(baselineInvalidDateResult));
// Large dataset handling
final largeDatasetTime = measureLargeDatasetCalculation();
expect(largeDatasetTime, lessThan(acceptableMaxTime));
}Success Validation Checklist
Performance Validation
- 40% improvement in overall service performance
- 30% reduction in memory usage
- No performance regression in any service
- Cache hit rate > 80% for repeated calculations
- Large dataset handling improved by 50%
Accuracy Validation
- All calculations return identical results to baseline
- Edge cases handled identically
- No floating point precision issues introduced
- Date/time calculations maintain accuracy
- Null and empty data handled correctly
Code Quality Validation
- 50% reduction in duplicate code lines
- All services use appropriate base classes
- No circular dependencies introduced
- Proper error handling maintained
- Documentation updated for new patterns
Compatibility Validation
- All public service APIs unchanged
- Constructor signatures preserved
- Return types identical
- Exception handling behavior preserved
- Thread safety maintained
Rollback Strategy
Immediate Rollback
- Keep original service files as
.backupduring development - Restore individual services if any metric fails
- Git branch strategy allows atomic rollbacks
Selective Rollback
- Rollback can be done per service category:
- Labor services independently
- Equipment services independently
- Log services independently
Validation Gates
- Each service optimization must pass all metrics before proceeding
- Automated testing prevents broken builds
- Performance regression detection stops deployment
Files Modified (Complete List)
New Base Service Files
lib/services/base/base_log_calculation_service.dartlib/services/base/base_time_calculation_service.dartlib/services/base/base_usage_service.dart
New Consolidated Services
lib/services/labor/labor_log_filter_service.dartlib/services/labor/optimized_labor_calculator_service.dart
Modified Existing Services (Labor - 13 files)
lib/services/labor/labor_daily_log_service.dart(optimized)lib/services/labor/labor_onsite_log_service.dart(uses consolidated)lib/services/labor/labor_traveling_log_service.dart(uses consolidated)lib/services/labor/labor_total_time_service.dart(optimized)lib/services/labor/labor_machine_time_service.dart(optimized)- All 7 time card services (use factory pattern)
Modified Equipment Services (5 files)
- All equipment services optimized for performance
Modified Log Services (8 files)
- All log services use new base classes
Total Files: 31 (5 new, 26 modified) Scope: Only service files - no providers, repositories, adapters, or UI touched