Phase B of architecture coupling refactor (epic AZ-309). Replaces the monolithic SatelliteProvider.Services with three per-component csprojs to add a compiler-enforced module boundary (resolves F4): - SatelliteProvider.Services.TileDownloader - SatelliteProvider.Services.RegionProcessing - SatelliteProvider.Services.RouteManagement DI registrations relocated into per-component AddTileDownloader / AddRegionProcessing / AddRouteManagement extension methods called from Program.cs. RateLimitException moved to Common/Exceptions/ to keep the three new csprojs as siblings (no Region->TileDownloader ProjectReference). Dockerfiles and consumer csprojs (Api, Tests) rewired to the new project paths. No DI lifetime or hosted-service order changes. Build: 0 warn, 0 err. Unit tests: 40/40. Smoke integration: green. Co-authored-by: Cursor <cursoragent@cursor.com>
6.8 KiB
Code Review Report
Batch: 5 (AZ-312, AZ-313, AZ-314 — coupling refactor: project split + DI extension methods) Date: 2026-05-10 Verdict: PASS
Scope
Structural refactoring (architecture baseline finding F4): split monolithic SatelliteProvider.Services into three per-component csprojs, rewire consumers, and extract DI registrations into per-component extension methods.
Tasks reviewed:
- AZ-312 — split Services into TileDownloader + RegionProcessing + RouteManagement csprojs
- AZ-313 — update consumer csprojs (Api, Tests, IntegrationTests) +
usingdirectives - AZ-314 — DI extension methods per csproj + Program.cs cleanup
Changed files
- New csprojs:
SatelliteProvider.Services.TileDownloader/SatelliteProvider.Services.TileDownloader.csprojSatelliteProvider.Services.RegionProcessing/SatelliteProvider.Services.RegionProcessing.csprojSatelliteProvider.Services.RouteManagement/SatelliteProvider.Services.RouteManagement.csproj
- New DI extension files:
SatelliteProvider.Services.TileDownloader/TileDownloaderServiceCollectionExtensions.csSatelliteProvider.Services.RegionProcessing/RegionProcessingServiceCollectionExtensions.csSatelliteProvider.Services.RouteManagement/RouteManagementServiceCollectionExtensions.cs
- Moved (with namespace updates):
TileService.cs,GoogleMapsDownloaderV2.cs,RegionService.cs,RegionProcessingService.cs,RegionRequestQueue.cs,RouteService.cs,RouteProcessingService.cs - New common exception:
SatelliteProvider.Common/Exceptions/RateLimitException.cs - Modified:
SatelliteProvider.Api/Program.cs,SatelliteProvider.Api/SatelliteProvider.Api.csproj,SatelliteProvider.Tests/SatelliteProvider.Tests.csproj, all 5*Tests.csfiles (usingupdates),SatelliteProvider.sln, bothDockerfiles - Deleted:
SatelliteProvider.Services/directory andSatelliteProvider.Services.csproj
Findings
| # | Severity | Category | File:Line | Title |
|---|
No findings.
Phase results
Phase 2 — Spec compliance
AZ-312 (project split)
- AC-1 ✓ — Three new csprojs exist with the seven moved source files distributed correctly (verified by
ls). - AC-2 ✓ — Old
SatelliteProvider.Services/directory and csproj deleted;SatelliteProvider.slnupdated. - AC-3 ✓ —
dotnet build SatelliteProvider.slnsucceeds with 0 warnings, 0 errors. - AC-4 ✓ — None of the three new csprojs reference each other. All three reference only
SatelliteProvider.Common+SatelliteProvider.DataAccess.
AZ-313 (consumer rewire)
- AC-1 ✓ — Solution builds clean (47.98s, 0 warnings).
- AC-2 ✓ — All 40 unit tests pass (1.93s).
- AC-3 ✓ — Grep for
using SatelliteProvider.Services;(no.<Component>suffix) returns zero source-file matches.
AZ-314 (DI extension methods)
- AC-1 ✓ — Each
*ServiceCollectionExtensions.csregisters exactly the services owned by its csproj:AddTileDownloader:IMemoryCache,ISatelliteDownloader,ITileServiceAddRegionProcessing:IRegionRequestQueue(factory),IRegionService,RegionProcessingService(hosted)AddRouteManagement:IRouteService,RouteProcessingService(hosted)
- AC-2 ✓ —
Program.cs:33-35calls the three extension methods; previously inlined registrations are gone. - AC-3 ✓ — Smoke integration suite passes end-to-end (region processing, route processing, tile ZIP, security tests). Every required service resolves at runtime.
- AC-4 ✓ — No old service-registration code remains in
Program.cs.
Phase 3 — Code quality
- SOLID (SRP/DIP) improved — components now have a compiler-enforced boundary; cross-component coupling can no longer be introduced silently.
- Lifetimes preserved —
SingletonstayedSingleton, hosted services stayed hosted services. - Hosted-service registration order preserved (
RegionProcessingServicebeforeRouteProcessingService, matching pre-refactor order in Program.cs). - No new dead code; no scope creep.
Phase 4 — Security quick-scan
Refactor is structural; no new input handling, query construction, or deserialization paths. Smoke security tests (SEC-01 SQL injection, SEC-02 path traversal, SEC-03 oversized region, SEC-04 malformed JSON) all pass post-refactor.
Phase 5 — Performance scan
No hot-path changes. No new allocations in the moved code (only namespace and project boundary changes).
Phase 6 — Cross-task consistency
- All three new extension methods follow the same
Add<Component>(this IServiceCollection)naming convention. - All three new csprojs share identical TFM (
net8.0),ImplicitUsings, andNullablesettings. - Package versions consistent (
9.0.10forMicrosoft.Extensions.*,13.0.4forNewtonsoft.Json,3.1.11forSixLabors.ImageSharp).
Phase 7 — Architecture compliance
- Layer direction ✓ — TileDownloader (Layer 2) imports only Common + DataAccess (Layer 1). RegionProcessing and RouteManagement (Layer 3) import only Common + DataAccess (no cross-Layer-3 references after the refactor — even better than the pre-refactor baseline allowed).
- Public API respect ✓ — All cross-component imports use interfaces from
SatelliteProvider.Common.Interfaces, never concrete types from sibling components. - No new cyclic dependencies ✓ — Graph is a clean DAG: Common ← {DataAccess} ← {TileDownloader, RegionProcessing, RouteManagement} ← WebApi.
- Architecture baseline F4 — RESOLVED. The single
SatelliteProvider.Services.csprojpacking three components is replaced by three csprojs with compiler-enforced boundaries. - Architecture baseline F3 — already resolved in Batch 4 (AZ-310 + AZ-311); this batch did not regress it (
Program.cstile endpoints still route throughITileService). RateLimitExceptionplacement — moved toSatelliteProvider.Common/Exceptions/rather than allowing aRegionProcessing → TileDownloaderProjectReference. This is the cleanest of the three options considered (move to Common / add reference / catch generalException) and preserves layer 3 components as siblings rather than coupling them.
Baseline Delta
| Status | Finding | Severity | Notes |
|---|---|---|---|
| Resolved | F4 — SatelliteProvider.Services.csproj packs three logical components |
Medium | Split into three csprojs |
| Carried over | (none for this batch) | — | — |
| Newly introduced | (none) | — | — |
module-layout.md and architecture.md still describe the pre-split layout (single SatelliteProvider.Services/ project). This is expected — documentation sync is task AZ-315 (Batch 3 of this refactor run). Not a finding.
Verdict
PASS — all 12 ACs across 3 tasks satisfied. Build clean. Unit tests 40/40. Smoke integration green. Architecture baseline F4 resolved with no new findings.