docs(elixir): prepare hierarchical refactor

This commit is contained in:
Schuwi
2025-09-17 23:32:46 +02:00
parent 5a1775e836
commit 963c9a3770
2 changed files with 524 additions and 0 deletions

View File

@@ -0,0 +1,154 @@
# 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
```elixir
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
```elixir
# 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)
```elixir
# 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)
```elixir
# 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:
1. **Simpler**: No virtual fields or complex batch operations
2. **More Performant**: O(depth) vs O(n) complexity
3. **Preventive**: UI-level cycle prevention vs reactive validation
4. **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:
1. **Simpler code** (50% fewer lines)
2. **Better performance** (O(depth) vs O(n))
3. **More maintainable** (functional vs stateful)
4. **Standard patterns** (Ecto associations vs virtual fields)
5. **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.