Skip to content

Refactor routing core: introduce RouteCollection and RouteFinder to reduce Router responsibilities #412

@armanist

Description

@armanist

Motivation

The current routing system works well functionally, but several responsibilities are tightly coupled across Router, RouteBuilder, RouteDispatcher, and RouteController.

In particular:

  • Router acts as a route registry, builder coordinator, matcher, and runtime authority
  • Route definition and runtime resolution phases are not clearly separated
  • Global helpers depend on RouteController holding runtime route state
  • Internal method calls rely on Router::getRoutes() as a global source of truth

This makes the routing subsystem harder to reason about, test, and evolve safely.

The goal of this refactor is not to redesign routing or the DSL, but to introduce clear internal boundaries while keeping current behavior and developer experience intact.


Goals

  • Reduce responsibilities of Router without changing the public DSL
  • Introduce a dedicated route storage abstraction (RouteCollection)
  • Introduce a stateless runtime route resolver (RouteFinder)
  • Clarify the lifecycle:
    • definition phase → runtime phase
  • Preserve all existing route helpers and runtime behavior

Non-Goals

  • ❌ No changes to route DSL syntax or semantics
  • ❌ No middleware redesign
  • ❌ No controller refactor
  • ❌ No removal of RouteController static runtime state
  • ❌ No console routing or non-HTTP concerns
  • ❌ No PSR-7 or DI refactors

Proposed Architectural Changes

1. RouteCollection (new core class)

Responsibilities:

  • Store finalized Route objects
  • Act as the single source of truth for all registered routes
  • Provide query operations (e.g. find by name, group existence)

Constraints:

  • Append-only during route definition
  • Read-only during runtime
  • No matching or dispatch logic

2. RouteFinder (new runtime class)

Responsibilities:

  • Match an HTTP request to a route from RouteCollection
  • Extract route parameters
  • Return a resolved route structure

Constraints:

  • Stateless
  • Does NOT store or expose global route state
  • Does NOT dispatch or execute controllers

3. Router (narrowed responsibilities)

Router should:

  • Remain the DSL entry point
  • Coordinate route definition using RouteBuilder
  • Own a RouteCollection instance
  • Delegate runtime matching to RouteFinder

Router should NOT:

  • Act as a route registry
  • Perform route matching itself
  • Serve as a runtime query API

Public API may remain temporarily for compatibility but should delegate internally.


4. RouteBuilder (definition-only)

  • Continues to support fluent DSL
  • Builds Route objects
  • Pushes finalized routes into RouteCollection
  • Must not leak mutable state beyond definition phase

5. RouteController (runtime context holder)

  • Continues to store the current resolved route
  • Continues to support existing global helpers
  • Receives resolved route data from dispatcher

No behavior change in this ticket.


6. RouteDispatcher (execution boundary)

  • Receives a resolved route + request
  • Executes middleware and controller
  • Does not perform route matching or lookup

Acceptance Criteria

  • All existing route DSL files work unchanged
  • All existing global route helpers continue to function
  • Route matching logic is moved out of Router
  • Router::getRoutes() usage is internally replaced with RouteCollection
  • RouteFinder is stateless and testable in isolation
  • Clear separation between definition and runtime phases exists internally

Tests

  • Unit tests for:
    • RouteCollection
    • RouteFinder
  • Integration tests verifying:
    • grouped routes
    • middleware inheritance
    • named routes
    • existing helpers (current_controller(), etc.)

Notes

This refactor intentionally avoids architectural extremism and focuses on clarity, containment, and future-safety rather than theoretical purity.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions