5.8 KiB
Hierarchical Implementation Analysis
Overview
This document analyzes the current hierarchical implementations in the codebase (Categories and Storage Locations) to determine the best approach for creating a shared Hierarchical behavior module.
Current State Analysis
Categories vs Storage Locations Feature Comparison
| Feature | Categories | Storage Locations | Assessment |
|---|---|---|---|
| Path Resolution | Simple recursive function in schema | Complex virtual fields + batch computation | Categories wins - cleaner approach |
| Cycle Prevention | UI-level filtering (preventive) | Changeset validation (reactive) | Categories wins - more efficient |
| Hierarchical Display | Recursive LiveView components | Recursive LiveView components | Tie - nearly identical |
| Parent Selection | Smart dropdown filtering | Smart dropdown filtering | Tie - identical logic |
| Performance | O(depth) recursive calls | O(n) batch computation + DB queries | Categories wins - simpler |
| Code Complexity | ~50 lines of hierarchy logic | ~100+ lines of hierarchy logic | Categories wins - more maintainable |
Key Findings
1. Vestigial is_active Field Removed
- Status: ✅ REMOVED
- Impact: Field was completely unused across the entire codebase
- Files Modified:
lib/components_elixir/inventory/storage_location.ex(schema and changeset)- Migration created:
20250917210658_remove_is_active_from_storage_locations.exs
2. Categories Implementation is Superior
Path Resolution Approach
Categories: Elegant recursive function in schema
def full_path(%Category{parent: nil} = category), do: category.name
def full_path(%Category{parent: %Category{} = parent} = category) do
"#{full_path(parent)} > #{category.name}"
end
Storage Locations: Over-engineered with virtual fields
# Virtual fields in schema
field :level, :integer, virtual: true
field :path, :string, virtual: true
# Complex batch computation in context
def compute_hierarchy_fields_batch(locations)
def compute_level_for_single(location)
def compute_path_for_single(location)
Cycle Prevention Strategy
Categories: Prevention at UI level (efficient)
# Prevents invalid options from appearing in dropdown
|> Enum.reject(fn cat ->
cat.id == editing_category_id ||
(editing_category_id && is_descendant?(categories, cat.id, editing_category_id))
end)
Storage Locations: Validation at changeset level (reactive)
# Validates after user attempts invalid selection
defp validate_no_circular_reference(changeset) do
# Complex validation logic that runs on every save attempt
end
3. Shared Patterns Identified
Both systems implement identical patterns for:
- Hierarchical tree display with recursive LiveView components
- Parent/child relationship filtering
- Descendant detection algorithms
- Root entity identification
- UI depth-based styling and icons
Architecture Decision
Recommended Approach: Category-Style Hierarchical Module
The category implementation should be the template for generalization because:
- Simpler: No virtual fields or complex batch operations
- More Performant: O(depth) vs O(n) complexity
- Preventive: UI-level cycle prevention vs reactive validation
- Maintainable: Half the lines of code with same functionality
AprilTag Features Remain Storage-Specific
The AprilTag system should NOT be generalized because:
- Physical identification is meaningless for categories
- Future scanning/detection features are location-specific
- Keeps domain separation clean
Implementation Complexity Comparison
Categories (Simple & Clean)
Files: 1 schema + 1 LiveView = 2 files
Lines: ~50 lines hierarchical logic
Approach: Functional, recursive, preventive
Dependencies: Standard Ecto associations
Storage Locations (Over-Engineered)
Files: 1 schema + 1 context helper + 1 LiveView = 3 files
Lines: ~100+ lines hierarchical logic
Approach: Stateful, batch processing, reactive
Dependencies: Virtual fields + custom computation
Performance Analysis
Path Resolution Performance
- Categories:
O(depth)- traverses only parent chain - Storage Locations:
O(n)- processes all entities for batch computation
Memory Usage
- Categories: Minimal - uses existing associations
- Storage Locations: Higher - virtual fields + intermediate computations
Database Queries
- Categories: Standard association preloading
- Storage Locations: Additional queries for path/level computation
Code Quality Assessment
Categories Strengths
✅ Single Responsibility: Each function does one thing
✅ Functional Style: Pure functions, no side effects
✅ Standard Patterns: Uses established Ecto association patterns
✅ Easy Testing: Simple recursive functions
✅ Performance: Minimal computational overhead
Storage Locations Issues
❌ Multiple Responsibilities: Virtual fields + validation + computation
❌ Complex State: Virtual fields require careful management
❌ Custom Patterns: Non-standard Ecto usage
❌ Hard Testing: Complex batch operations
❌ Performance: Unnecessary computational overhead
Conclusion
The categories implementation is objectively superior and should guide the refactoring:
- Simpler code (50% fewer lines)
- Better performance (O(depth) vs O(n))
- More maintainable (functional vs stateful)
- Standard patterns (Ecto associations vs virtual fields)
- Preventive design (UI filtering vs changeset validation)
The storage locations system should be refactored to match the categories approach, eliminating virtual fields and complex batch computations in favor of simple recursive functions.