[AZ-365] Refactor C12: decompose RouteService.CreateRouteAsync

Extract RouteValidator (aggregating validator), RoutePointGraphBuilder
(point interpolation + sequence numbering), GeofenceGridCalculator
(NW/SE region centers), and RouteResponseMapper (entity -> DTO; also
used by GetRouteAsync, eliminating duplicate DTO assembly).

CreateRouteAsync shrinks 184 -> 52 LOC of orchestration. RouteService.cs
shrinks 295 -> 138 LOC overall. Validation aggregates all failures into
a single ArgumentException (AC-2); single-violation messages preserved
verbatim so existing RouteServiceTests pass unchanged. 28 new unit
tests for the four helpers (112/112 unit tests, smoke green).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-11 02:08:21 +03:00
parent d327000fb6
commit f7ad7aa5ab
11 changed files with 936 additions and 206 deletions
@@ -1,71 +0,0 @@
# Refactor: decompose RouteService.CreateRouteAsync 165-LOC method
**Task**: AZ-365_refactor_decompose_route_create_method
**Name**: Split CreateRouteAsync into validator + builder + grid + mapper
**Description**: Extract `RouteValidator`, `RoutePointGraphBuilder`, `GeofenceGridCalculator`, and `RouteResponseMapper` from `RouteService.CreateRouteAsync`.
**Complexity**: 5 points
**Dependencies**: None
**Component**: Services.RouteManagement
**Tracker**: AZ-365
**Epic**: AZ-350
## Problem
`SatelliteProvider.Services.RouteManagement/RouteService.cs:27-211` is a 165-LOC method that does input validation (4 separate rules, ~25 LOC of nested `if` chains), point-graph construction with `GeoUtils.CalculateIntermediatePoints`, route entity persistence, route-points persistence, geofence polygon validation, geofence grid generation, geofence region requests, and response mapping. Five distinct responsibilities in one method.
## Outcome
- `CreateRouteAsync` is reduced to orchestration of the four extracted helpers (~30-50 LOC).
- Validation aggregates errors instead of short-circuiting on the first.
- Each helper is unit-testable in isolation.
- 37 unit + 5 smoke tests stay green.
## Scope
### Included
- Extract `RouteValidator` (all `ArgumentException`-throwing checks; aggregates errors instead of short-circuiting).
- Extract `RoutePointGraphBuilder` (interpolation + sequence numbering — pure).
- Extract `GeofenceGridCalculator` (NW/SE → list of region centers — promote the existing private method).
- Extract `RouteResponseMapper` (entity → DTO; eliminates duplication with `GetRouteAsync`).
- Add unit tests for each helper.
### Excluded
- Changing the response shape.
- Changing the persistence calls.
- Changing the geofence semantics.
## Acceptance Criteria
**AC-1: CreateRouteAsync is now an orchestrator**
Given the post-refactor source
When `CreateRouteAsync` is inspected
Then it is reduced to ~30-50 LOC of `_validator.Validate(...)`, `_pointGraphBuilder.Build(...)`, `_geofenceGridCalculator.GenerateRegions(...)`, `_responseMapper.Map(...)` calls (or equivalent).
**AC-2: Validation aggregates errors**
Given an input with multiple validation failures
When validated
Then all failures are collected and surfaced as a single 400 response (still typed as `ArgumentException` or a typed `ValidationException`).
**AC-3: Same persistence + same response**
Given any input that succeeds today
When the post-refactor code runs
Then the same DB rows are created and the same response shape is returned.
**AC-4: Tests stay green**
Given the post-refactor build
When `scripts/run-tests.sh --smoke` runs
Then all 37 unit + 5 smoke scenarios pass; `RouteServiceTests` is unchanged.
## Constraints
- Same persistence calls.
- Same response shape (no DTO change).
- 400 status preserved for validation failures.
## Risks & Mitigation
**Risk 1: aggregated validation surfaces multiple errors but tests assert on first**
- *Risk*: existing tests may assert on a specific single-error message.
- *Mitigation*: update test assertions to allow a list of errors.
Full change entry: `_docs/04_refactoring/03-code-quality-refactoring/list-of-changes.md` (C12).