The SCORM 2004 sequencing implementation has several "god classes" that are too large, have too many responsibilities, and contain significant code duplication. This makes the code:
- Hard to test - Coverage is stuck at ~70% because duplicate code paths mean tests hit one implementation but miss the copy
- Hard to maintain - Changes require updating multiple places
- Hard to understand - 3000+ line files with 60-119 methods each
| File | Lines | Methods | Coverage | Problem |
|---|---|---|---|---|
overall_sequencing_process.ts |
3,733 | 119 | 83% | Orchestrates everything, duplicates validation |
sequencing_process.ts |
3,036 | 63 | 70% | Duplicated constraint validation logic |
Scorm2004API.ts |
3,253 | 65 | 86% | API + business logic mixed |
activity.ts |
2,222 | ~60 | 95% | Data + behavior + validation mixed |
rollup_process.ts |
1,718 | 41 | 83% | Large but more focused |
BaseAPI.ts |
1,915 | 20 | 92% | Acceptable size for base class |
The same constrainChoice, forwardOnly, and preventActivation checks appear in multiple places:
Lines 569-578: choiceSequencingRequestProcess() - constrainChoice backward check
Lines 2932-2937: validateConstraintsAtAncestorLevel() - SAME CHECK, different path
Tests hit lines 569-578 and pass, but lines 2932-2937 remain uncovered because they're duplicate code reached via getAvailableChoices().
All of these validate choice constraints with slight variations:
validateChoicePathConstraints()- 82 linesvalidateConstraintsAtAncestorLevel()- 81 linesvalidateConstrainChoiceForFlow()- 83 linesevaluateConstrainChoiceForTraversal()- 74 linescheckConstrainedChoiceBoundary()- 111 lines
Total: 431 lines of overlapping validation logic
evaluateRuleConditions()- 237 lineschoiceSequencingRequestProcess()- 200 linesflowTreeTraversalSubprocess()- 129 linescheckConstrainedChoiceBoundary()- 111 lines
Create a single ChoiceConstraintValidator class that handles ALL constraint validation:
// New file: src/cmi/scorm2004/sequencing/validators/choice_constraint_validator.ts
export class ChoiceConstraintValidator {
validateConstrainChoice(ancestor: Activity, current: Activity, target: Activity): ValidationResult
validateForwardOnly(ancestor: Activity, current: Activity, target: Activity): ValidationResult
validatePreventActivation(ancestor: Activity, target: Activity): ValidationResult
validateAllAncestorConstraints(current: Activity, target: Activity): ValidationResult
}This eliminates the 5 duplicate methods and 21 scattered constraint checks.
Break sequencing_process.ts into focused request handlers:
src/cmi/scorm2004/sequencing/
├── sequencing_process.ts # Coordinator only (~500 lines)
├── handlers/
│ ├── choice_request_handler.ts # All choice logic
│ ├── flow_request_handler.ts # Continue/Previous
│ ├── exit_request_handler.ts # Exit/ExitAll/Abandon
│ └── retry_request_handler.ts # Retry/RetryAll
├── validators/
│ ├── choice_constraint_validator.ts
│ └── navigation_validator.ts
└── traversal/
├── flow_traversal.ts
└── choice_traversal.ts
This 3,733-line file orchestrates the sequencing loop. Extract:
- Termination logic →
termination_handler.ts - Delivery logic →
delivery_handler.ts - Navigation validity →
navigation_validity_service.ts
Current tests exercise the god classes through high-level entry points. After refactoring:
- Unit tests for each extracted class (validators, handlers, traversal)
- Integration tests for the coordinator classes
- Remove duplicate test scenarios that exist only because code was duplicated
- No source file > 800 lines
- No class > 30 methods
- Zero duplicated validation logic
- Coverage > 85% on all sequencing files
- All existing tests still pass
Before planning, read these to understand the current structure:
/src/cmi/scorm2004/sequencing/sequencing_process.ts- Lines 440-600 (choice handling)/src/cmi/scorm2004/sequencing/sequencing_process.ts- Lines 2800-2950 (duplicate validation)/src/cmi/scorm2004/sequencing/overall_sequencing_process.ts- Lines 1-200 (main loop)/test/cmi/scorm2004/sequencing/sequencing_process.spec.ts- Understand test patterns
- Must maintain SCORM 2004 4th Edition compliance
- Must not break existing public API (
Scorm2004API) - Must maintain backward compatibility with existing LMS integrations
- Incremental refactoring preferred (one extraction at a time, tests passing between each)
- Which extraction provides the most value with least risk?
- Can we refactor incrementally or does it need to be all-at-once?
- Are there hidden dependencies between the duplicate code paths?
- Should we add integration tests before refactoring as a safety net?