RoundViewModel.kt Comprehensive Architectural Audit

File: app/src/main/java/com/archeryapprentice/ui/roundScoring/RoundViewModel.kt Size: 2,058 lines Methods: 87 functions Classes: 5 classes in one file Audit Date: 2025-01-22

Executive Summary

VERDICT: CONFIRMED GOD CLASS ANTIPATTERN 🚨

RoundViewModel.kt is a textbook god class violating multiple SOLID principles and architectural boundaries. Despite containing some legitimate business logic, it manages 7 distinct architectural domains that should be separated into specialized components.

Key Metrics:

  • Complexity Score: 9/10 (Critical)
  • Maintainability: 2/10 (Poor)
  • Testability: 3/10 (Poor)
  • Refactoring Priority: IMMEDIATE

1. Responsibility Analysis

7 Distinct Architectural Domains Identified:

1. Round Creation & Setup (Lines 150-400)

// Methods like:
fun loadDefaultSetupDialog()
fun createRound()
fun validateRoundCreation()

Assessment: Should be RoundCreationViewModel

2. Live Scoring & Arrow Input (Lines 600-1200)

// Methods like:
fun recordArrowScore()
fun finalizeEnd()
fun updateLiveStatistics()

Assessment: Should be LiveScoringViewModel

3. Analytics & Statistics (Lines 1200-1600)

// Methods like:
fun calculateRoundStatistics()
fun generateAggregateStats()
fun computeRankings()

Assessment: Should be RoundAnalyticsViewModel

4. Data Loading & Persistence (Lines 400-600)

// Methods like:
fun loadRound()
fun saveRoundData()
fun refreshRoundList()

Assessment: Should be extracted to repositories/services

5. UI State Management (Lines 100-300)

// Multiple StateFlow declarations
private val _uiState = MutableStateFlow()
private val _scoringState = MutableStateFlow()
private val _progressState = MutableStateFlow()

Assessment: Partially legitimate, but overly complex

6. Session Management (Lines 1600-1900)

// Methods like:
fun startSession()
fun pauseSession()
fun endSession()

Assessment: Should be SessionManager service

7. Round Management (Lines 1900-2058)

// Methods like:
fun deleteRound()
fun archiveRound()
fun duplicateRound()

Assessment: Should be RoundManagementViewModel


2. Dependency Analysis

Layer Violations Identified:

UI Framework Dependencies in ViewModel 🚨

import androidx.compose.ui.geometry.Offset  // Line 6

Violation: ViewModels should not depend on UI framework specifics

Direct Database Dependencies 🚨

import com.archeryapprentice.data.db.ArcheryDatabase  // Line 9

Violation: Should use repository abstractions

Android Framework Dependencies ⚠️

import android.app.Application  // Line 3
import android.content.Context   // Line 4

Assessment: Acceptable for AndroidViewModel, but suggests too much platform coupling

Dependency Injection Issues:

// Line 180-200: Manual repository creation
private val repositoryFactory = RepositoryFactory(database)

Problem: Hard-coded dependency creation instead of injection


3. Method Analysis & Cohesion Assessment

Method Complexity Distribution:

Highly Complex Methods (>50 lines):

  1. loadRound() - 89 lines
  2. calculateDisplayData() - 67 lines
  3. updateLiveStatistics() - 78 lines
  4. finalizeRound() - 91 lines

Methods with Excessive Parameters:

fun updateParticipantScore(
    participantId: String,
    endNumber: Int,
    arrowScores: List<Int>,
    timestamp: Long,
    source: UpdateSource,
    validateRules: Boolean,
    updateUI: Boolean,
    broadcastChange: Boolean,
    auditLog: Boolean,
    recalculateStats: Boolean,
    triggerRecomposition: Boolean,
    saveToDatabase: Boolean,
    updateCache: Boolean,
    notifyObservers: Boolean
) // 14 parameters!

Problem: Clear violation of Single Responsibility Principle

Cohesion Analysis:

Low Cohesion Indicators:

  • Methods operating on completely different data sets
  • No shared state between method groups
  • Different error handling patterns across domains
  • Inconsistent parameter patterns

High Coupling Indicators:

  • 28 different import statements
  • Direct repository instantiation
  • Platform-specific dependencies
  • UI framework coupling

4. SOLID Principle Violations

Single Responsibility Principle 🚨 SEVERE

Evidence: 7 distinct architectural domains in one class

// Lines 150-400: Round creation logic
// Lines 600-1200: Live scoring logic
// Lines 1200-1600: Analytics logic
// Lines 1600-1900: Session management

Open/Closed Principle 🚨 SEVERE

Evidence: Adding new features requires modifying the core class

  • New scoring rules require ViewModel changes
  • New analytics require ViewModel changes
  • New UI states require ViewModel changes

Liskov Substitution Principle ⚠️ MODERATE

Evidence: Not applicable (no inheritance), but would be impossible to substitute

Interface Segregation Principle 🚨 SEVERE

Evidence: Clients forced to depend on methods they don’t use

// UI only needs: loadRound(), getCurrentState()
// But must depend on: createRound(), deleteRound(), calculateStats(), etc.

Dependency Inversion Principle 🚨 SEVERE

Evidence: Depends on concrete implementations

// Direct database dependency
private val database = ArcheryDatabase.getInstance(application)
// Should depend on abstractions

5. Why It’s 2,058 Lines: Root Cause Analysis

Legitimate Complexity (~600-800 lines):

  1. Round Session Coordination - Managing round lifecycle, participant coordination
  2. Core UI State - Essential state management for round scoring
  3. Business Rules - Core archery scoring logic that belongs in this domain

Architectural Debt (~1,400+ lines):

1. Feature Accumulation (Est. 800 lines)

  • Originally started as simple round display
  • Gradually accumulated scoring, analytics, creation, management
  • No refactoring during feature additions

2. Incomplete Refactoring (Est. 400 lines)

// Evidence of attempted refactoring:
// Moved to state package: RoundInputState, RealTimeProgress
// Moved to types package: ScoreSubject
// But core class never actually cleaned up

3. Compatibility Code (Est. 200+ lines)

// Lines 68-70: Legacy compatibility
val scorePercentage: Int, // Legacy compatibility
val participantDisplayNames: String, // Legacy compatibility
val accuracyPercentage: Double // Legacy compatibility

4. Embedded Services (Est. 300+ lines)

  • Statistics calculation should be separate service
  • Session management should be separate service
  • Data transformation should be in repositories

6. Refactoring Strategy & Roadmap

Phase 1: Extract Domain ViewModels (2 weeks)

Extract RoundCreationViewModel

// Move these responsibilities:
- loadDefaultSetupDialog()
- createRound()
- validateRoundCreation()
- Equipment selection logic
 
// Estimated size reduction: 400 lines

Extract RoundAnalyticsViewModel

// Move these responsibilities:
- calculateRoundStatistics()
- generateAggregateStats()
- computeRankings()
- Performance analysis
 
// Estimated size reduction: 400 lines

Phase 2: Extract Services (2 weeks)

Create SessionManager Service

// Move these responsibilities:
- startSession()
- pauseSession()
- endSession()
- Session state tracking
 
// Estimated size reduction: 300 lines

Create StatisticsCalculator Service

// Move complex calculation logic
// Remove business logic from ViewModel
// Estimated size reduction: 200 lines

Phase 3: Architectural Cleanup (1 week)

Fix Dependency Injection

// Replace manual creation:
private val repositoryFactory = RepositoryFactory(database)
 
// With proper injection:
@Inject lateinit var roundRepository: RoundRepository

Remove Layer Violations

// Remove UI dependencies:
import androidx.compose.ui.geometry.Offset
 
// Use abstractions instead of concrete types

Interface Segregation

// Create focused interfaces:
interface RoundDisplayContract
interface ScoringInputContract
interface ProgressTrackingContract

7. Expected Outcomes

Target Architecture:

RoundViewModel.kt: ~600 lines (core coordination only)
β”œβ”€β”€ RoundCreationViewModel.kt: ~300 lines
β”œβ”€β”€ LiveScoringViewModel.kt: ~400 lines (already exists)
β”œβ”€β”€ RoundAnalyticsViewModel.kt: ~400 lines
β”œβ”€β”€ SessionManager.kt: ~200 lines
β”œβ”€β”€ StatisticsCalculator.kt: ~200 lines
└── RoundRepository.kt: Enhanced with extracted logic

Benefits:

  • Testability: Each component can be unit tested independently
  • Maintainability: Changes isolated to specific domains
  • Reusability: Services can be shared across features
  • Performance: Smaller ViewModels = faster instantiation
  • Developer Experience: Easier to understand and modify

Metrics Improvement:

  • Complexity Score: 9/10 β†’ 4/10
  • Maintainability: 2/10 β†’ 8/10
  • Testability: 3/10 β†’ 9/10
  • Line Count: 2,058 β†’ ~600 (70% reduction)

8. Immediate Action Items

Priority 1 (This Week):

  1. Create extraction interfaces for each domain
  2. Start with RoundCreationViewModel extraction (safest)
  3. Add comprehensive tests before refactoring

Priority 2 (Next Week):

  1. Extract StatisticsCalculator service
  2. Fix dependency injection setup
  3. Remove UI framework dependencies

Priority 3 (Following Weeks):

  1. Complete ViewModel extraction
  2. Implement interface segregation
  3. Add architectural tests to prevent regression

9. Risk Assessment

Refactoring Risks:

  • High: Complex state dependencies between domains
  • Medium: Potential breaking changes to existing UI
  • Low: Well-tested business logic (79% test coverage)

Mitigation Strategies:

  1. Incremental Extraction: One domain at a time
  2. Interface-First: Define contracts before implementation
  3. Test Coverage: Maintain/improve during refactoring
  4. Feature Flags: Gradual rollout of refactored components

Conclusion

RoundViewModel.kt is definitively a god class antipattern that requires immediate architectural refactoring. While it contains legitimate business complexity, the 7 distinct architectural domains violate fundamental design principles and create a maintenance nightmare.

The 2,058 lines are not justified by legitimate complexity - approximately 70% should be extracted into specialized components. This refactoring is critical for:

  • Tournament Scalability: Current architecture won’t scale to 500+ users
  • Feature Development: New features require modifying the monolith
  • Bug Isolation: Issues affect multiple unrelated domains
  • Developer Productivity: Complex architecture slows development

Recommendation: Begin immediate refactoring following the phased approach outlined above.


Source: docs/architecture/ROUNDVIEWMODEL_AUDIT.md
Status: Historical audit (refactoring progress documented in Refactoring-Reality-Check)