Skip to content

Simplify VarietyRepository

Priority: 🟠 HIGH Status: Planning Related QA Analysis: qa-analysis-overview.md

Problem Statement

Repository/VarietyRepository.php:132 has excessive complexity in query building:

Violations

  • Method: findWithFiltersAndPagination() (line 132)
  • Cyclomatic Complexity: 15/10 (50% over threshold)
  • NPath Complexity: 1,215/250 (386% over threshold!)

Impact

  • Query Correctness: Complex query building prone to bugs
  • Performance: Difficult to optimize complex queries
  • Maintainability: Hard to add new filters or modify query logic
  • Testing: Difficult to test all filter combinations

Guideline Violations

  • SOLID - Single Responsibility Principle: Method doing too much
  • Repository Pattern: Business logic creeping into data access layer

Root Cause Analysis

Based on method name, findWithFiltersAndPagination() likely handles:

  1. Multiple filter types (variety name, region, processing, etc.)
  2. Conditional query building based on active filters
  3. Join logic for related entities
  4. Pagination logic (limit, offset, ordering)
  5. Possibly sorting options
  6. Complex WHERE clause construction
  7. Count queries for pagination

High NPath (1,215) suggests many conditional branches for different filter combinations.

Proposed Refactoring Strategy

Step 1: Analyze Current Method

  • Document all supported filters
  • Identify query building patterns
  • Map conditional logic
  • Review join requirements
  • Check for duplication

Step 2: Extract Query Builder Pattern

Create dedicated query builder:

class VarietyQueryBuilder
{
    private QueryBuilder $qb;

    public function create(): self;
    public function withNameFilter(?string $name): self;
    public function withRegionFilter(?int $regionId): self;
    public function withProcessingFilter(?array $methods): self;
    public function withPagination(int $page, int $limit): self;
    public function withSorting(string $field, string $direction): self;
    public function build(): Query;
    public function count(): int;
}

Step 3: Extract Filter Specifications

Use Specification pattern for complex filters:

interface VarietySpecification
{
    public function apply(QueryBuilder $qb): void;
}

class NameSpecification implements VarietySpecification { ... }
class RegionSpecification implements VarietySpecification { ... }

Step 4: Simplify Repository Method

Reduce complexity by using builder:

public function findWithFiltersAndPagination(
    VarietyFilterCriteria $criteria
): PaginatedResult {
    $builder = $this->createQueryBuilder()
        ->withFilters($criteria)
        ->withPagination($criteria->page, $criteria->limit)
        ->withSorting($criteria->sortBy, $criteria->direction);

    return new PaginatedResult(
        results: $builder->build()->getResult(),
        total: $builder->count(),
        page: $criteria->page,
        limit: $criteria->limit
    );
}

Step 5: Create Filter Criteria DTO

Encapsulate filter parameters:

class VarietyFilterCriteria
{
    public function __construct(
        public readonly ?string $name = null,
        public readonly ?int $regionId = null,
        public readonly ?array $processingMethods = null,
        public readonly int $page = 1,
        public readonly int $limit = 20,
        public readonly string $sortBy = 'name',
        public readonly string $direction = 'ASC'
    ) {}
}

Success Criteria

  • findWithFiltersAndPagination() cyclomatic complexity < 10
  • NPath complexity < 250
  • Method < 30 lines
  • Clear separation of query building logic
  • Easy to add new filters
  • Improved testability
  • No regression in query results or performance
  • Query performance maintained or improved

Testing Strategy

Before Refactoring

  • Add tests for all current filter combinations
  • Test pagination edge cases
  • Test sorting options
  • Benchmark query performance

After Refactoring

  • Unit test query builder
  • Unit test specifications
  • Verify integration tests pass
  • Benchmark query performance (ensure no regression)
  • Test new filter additions (verify ease of extension)

Risk Assessment

Medium Risk:

  • Repository is core data access layer
  • Query changes could affect results
  • Performance impact possible
  • Many filter combinations to test

Mitigation:

  • Comprehensive test coverage before refactoring
  • Performance benchmarks before/after
  • Test with production-like data volumes
  • Verify SQL generated is equivalent
  • Use database query profiling

Estimated Effort

Medium:

  • Analysis & test coverage: 1 day
  • Query builder implementation: 1.5 days
  • Specification pattern (if used): 1 day
  • Repository refactoring: 0.5 day
  • Testing & verification: 1 day
  • Performance testing: 0.5 day
  • Total: 5.5 days

Implementation Order

  1. Add comprehensive tests for current behavior
  2. Benchmark current query performance
  3. Create query builder
  4. Implement filter specifications (optional)
  5. Refactor repository method
  6. Verify tests pass
  7. Benchmark performance
  8. Document new approach

Alternative Approaches

  • Fluent interface for query construction
  • Easy to test and extend
  • Clear separation of concerns

Option B: Specification Pattern

  • More flexible for complex business rules
  • Reusable specifications
  • More overhead, may be over-engineering

Option C: Criteria Object Only

  • Simpler than builder/specification
  • Less flexible for future extensions
  • May not reduce complexity enough

Recommendation: Start with Query Builder (Option A), add Specifications if needed later.

Dependencies

None - can be addressed independently

  • Repository/RegionRepository.php:89 also has complexity (CC: 11)
    • May have similar query building issues
    • Can apply same refactoring pattern
    • Address after VarietyRepository

Notes

  • High NPath (1,215) indicates many filter combinations - comprehensive testing critical
  • Query builder pattern is well-established in Doctrine/Symfony
  • Consider creating base query builder for reuse across repositories
  • May reveal opportunities for query optimization
  • Could improve API filter response times
  • Pattern can be replicated for other repositories (RegionRepository, etc.)