Container Registry: Separate legacy filesystem and database code paths in HTTP handlers
### Summary
The Container Registry HTTP handlers use if/else branches throughout their methods to switch between two metadata backends: the database and the legacy filesystem. This effort will separate these code paths to improve maintainability and reduce regression risk.
### Problem
The current branching pattern creates several challenges:
* **Cognitive overhead**: Developers must track which code executes under which conditions while reading handler logic
* **Intermingled code**: Database code sits alongside filesystem code, often in multiple sections within the same method
* **Maintenance risk**: Changes to database functionality can inadvertently affect legacy behavior
* **Unnecessary complexity**: Backend selection happens at startup and never changes, yet branching logic is repeated throughout request handling
### Proposed Solution
Refactor HTTP handlers to separate the two code paths:
1. **Extract legacy code**: Move filesystem logic into `Legacy*` methods (e.g., `LegacyStartBlobUpload`, `LegacyHandleGetTags`)
2. **Isolate in dedicated files**: Put legacy methods in `legacy_*.go` files (e.g., `legacy_blobupload.go`, `legacy_tags.go`)
3. **Simplify main handlers**: Main handler methods contain only database logic
4. **Dispatcher-level selection**: The dispatcher chooses the appropriate handler method once per request
### Scope
| Package | Conditional Checks | Handlers Affected |
|---------|--------------------|-------------------|
| `registry/handlers/` | \~27 | manifests, blobs, tags, catalog, blob uploads, repositories |
| `notifications/` | 3 | flag propagation |
| `registry/storage/` | 3 | filesystem linking |
**Out of scope**: Legacy conditional logic in `app.go` and `registry/storage` is harder to isolate and occurs less often.
### Rollout Strategy
Follow the incremental approach from the Database Load Balancing (DLB) rollout:
1. Refactor handlers from most to least traffic on GitLab.com
2. Merge one handler refactor per release cycle
3. Validate change on .com and self-managed
### Benefits
* Each code path is self-contained and easier to follow
* Changes to one backend can't accidentally affect the other
* `legacy_*.go` naming makes deprecated code obvious
* No new tests required—conformance tests already cover both paths
### Trade-offs
* Code duplication until legacy filesystem support is retired (potentially years)
* One-refactor-per-release cadence means this spans multiple milestones
* Legacy logic in `app.go` and `registry/storage` stays out of scope
### Success Criteria
* All handler methods in `registry/handlers/*.go` contain only database logic
* All legacy filesystem logic lives in `legacy_*.go` files
* Backend selection occurs at the dispatcher level, not within handler methods
* API conformance tests pass for both database and legacy configurations
### Related
* Parent initiative: &9857 (Refactoring Opportunities)
* Proof of concept: [!2766](https://gitlab.com/gitlab-org/container-registry/-/merge_requests/2766) (demonstrates pattern with tags and blob upload handlers)
* Prior art: [!699](https://gitlab.com/gitlab-org/container-registry/-/merge_requests/699) (earlier manifest handler refactor attempt)
epic