# List of Changes

**Run**: 02-coupling-refactoring
**Mode**: automatic (Targeted — `_docs/02_document/` already documents the codebase)
**Source**: self-discovered (driven by `_docs/02_document/architecture_compliance_baseline.md` findings F3 and F4)
**Date**: 2026-05-10

## Summary

Resolves the two remaining Medium architecture findings from the baseline scan: route the tile-serving and tile-download endpoints through `ITileService`, and split `SatelliteProvider.Services` into three per-component csprojs to add a compiler-enforced module boundary.

## Discovery shortcut (Targeted-mode skip)

Phase 1 sub-steps 1a, 1b, 1c are **skipped** because:

- Component documentation already exists at `_docs/02_document/components/01_common/` … `05_route_management/` (5 components fully documented).
- Solution synthesis already exists at `_docs/01_solution/solution.md`.
- System flows already exist at `_docs/02_document/system-flows.md`.
- Architecture vision and module-layout already exist at `_docs/02_document/architecture.md` and `_docs/02_document/module-layout.md`.

Logical-flow analysis was performed implicitly in Step 7's smoke run (real flows traced end-to-end through the actual endpoints, all passed). No silent data loss, loop scoping bugs, fixed-vs-dynamic batch issues, or design contradictions were observed — these aren't the failure modes this codebase exhibits.

## Changes

### C01: Add `GetOrDownloadTileAsync(z, x, y)` to ITileService and route ServeTile through it

- **File(s)**: `SatelliteProvider.Common/Interfaces/ITileService.cs`, `SatelliteProvider.Services/TileService.cs`, `SatelliteProvider.Api/Program.cs` (`ServeTile` handler), `SatelliteProvider.Tests/TileServiceTests.cs`
- **Problem**: `ServeTile` (Program.cs:141) directly injects `ISatelliteDownloader`, `ITileRepository`, and `IMemoryCache`. It re-implements cache-or-download-or-fetch logic that overlaps with `TileService.DownloadAndStoreTilesAsync` but is not delegated to the service. This is the architecture baseline F3 finding.
- **Change**: Add `Task<TileBytes> GetOrDownloadTileAsync(int z, int x, int y, CancellationToken ct = default)` to `ITileService`. The method returns a `TileBytes` record `(byte[] Bytes, string ContentType, string ETag, TimeSpan MaxAge)`. Implementation in `TileService` owns the in-memory cache (DI-injected `IMemoryCache`), repository lookup by tile coords, and downloader fallback. `ServeTile` becomes a thin handler: validates the route, calls `tileService.GetOrDownloadTileAsync`, sets headers, returns `Results.Bytes`.
- **Rationale**: All tile retrieval flows go through one place. New consumers (e.g. a future batch tile pre-warm endpoint) reuse the same caching/dedup logic. Aligns with the architecture vision in `_docs/02_document/architecture.md`.
- **Constraint Fit**: Public HTTP route `/tiles/{z}/{x}/{y}` and response shape (image/jpeg, ETag, Cache-Control) are preserved exactly. No DB schema change. Existing smoke test for tile serving still covers the path.
- **Risk**: low — pure code movement plus one new interface method. Smoke + unit suite catch regressions.
- **Dependencies**: None.

### C02: Add `DownloadAndStoreSingleTileAsync(lat, lon, zoom)` to ITileService and route GetTileByLatLon through it

- **File(s)**: `SatelliteProvider.Common/Interfaces/ITileService.cs`, `SatelliteProvider.Services/TileService.cs`, `SatelliteProvider.Api/Program.cs` (`GetTileByLatLon` handler), `SatelliteProvider.Tests/TileServiceTests.cs`
- **Problem**: `GetTileByLatLon` (Program.cs:206) directly injects `ISatelliteDownloader` and `ITileRepository` and re-implements the download-then-insert flow. Same root cause as C01.
- **Change**: Add `Task<TileMetadata> DownloadAndStoreSingleTileAsync(double latitude, double longitude, int zoomLevel, CancellationToken ct = default)` to `ITileService`. Implementation in `TileService` calls the existing `ISatelliteDownloader.DownloadSingleTileAsync` and inserts the resulting entity via `ITileRepository`. `GetTileByLatLon` becomes a thin handler that calls the new method and projects to `DownloadTileResponse`.
- **Rationale**: Same as C01.
- **Constraint Fit**: HTTP route `/api/satellite/tiles/latlon`, query parameters, and the `DownloadTileResponse` shape are preserved exactly. Zoom validation in `GoogleMapsDownloaderV2` keeps firing (via `ISatelliteDownloader.DownloadSingleTileAsync`).
- **Risk**: low — strictly smaller than C01 since there's no cache layer involved.
- **Dependencies**: C01 (so we add both interface methods in one PR scope but they're independently shippable).

### C03: Create three new csprojs for the Services split — `Services.TileDownloader`, `Services.RegionProcessing`, `Services.RouteManagement`

- **File(s)**:
  - New: `SatelliteProvider.Services.TileDownloader/SatelliteProvider.Services.TileDownloader.csproj`
  - New: `SatelliteProvider.Services.RegionProcessing/SatelliteProvider.Services.RegionProcessing.csproj`
  - New: `SatelliteProvider.Services.RouteManagement/SatelliteProvider.Services.RouteManagement.csproj`
  - Move from `SatelliteProvider.Services/`: `TileService.cs`, `GoogleMapsDownloaderV2.cs` → TileDownloader; `RegionService.cs`, `RegionProcessingService.cs`, `RegionRequestQueue.cs` → RegionProcessing; `RouteService.cs`, `RouteProcessingService.cs` → RouteManagement
  - Delete: `SatelliteProvider.Services/SatelliteProvider.Services.csproj`
- **Problem**: `SatelliteProvider.Services` packs three logical components into one csproj with no compiler-enforced boundary. This is architecture baseline F4. The module-layout doc already aspires to per-component boundaries.
- **Change**: Create three new csprojs. Each references `SatelliteProvider.Common` and `SatelliteProvider.DataAccess` (where applicable). Move the seven files into the corresponding new project. Update namespaces to `SatelliteProvider.Services.TileDownloader`, `SatelliteProvider.Services.RegionProcessing`, `SatelliteProvider.Services.RouteManagement`.
- **Rationale**: Compiler now rejects accidental cross-component coupling at build time, not at code-review time. Aligns the physical layout with `_docs/02_document/module-layout.md`.
- **Constraint Fit**: Consumers (`SatelliteProvider.Api`, `SatelliteProvider.Tests`) update their `ProjectReference` and `using` lists. No public HTTP API change. No DB change. The `ISatelliteDownloader` and other interfaces stay in `SatelliteProvider.Common` (unchanged).
- **Risk**: medium — touches every consumer's csproj and most of `Program.cs` namespaces. Mitigated by:
  - Clear, mechanical rename rules.
  - Both unit and smoke suites must pass after the move.
  - Solution-level `dotnet build` catches every missed reference.
- **Dependencies**: C02 (do the endpoint refactor first, while everything is still in one csproj — simpler to refactor before splitting).

### C04: Update `SatelliteProvider.Tests` and `SatelliteProvider.IntegrationTests` to reference the new Services projects

- **File(s)**: `SatelliteProvider.Tests/SatelliteProvider.Tests.csproj`, `SatelliteProvider.IntegrationTests/SatelliteProvider.IntegrationTests.csproj`, every `.cs` test file under those directories that imports a moved class
- **Problem**: Once C03 lands, the `SatelliteProvider.Services` csproj no longer exists. Test projects currently reference it.
- **Change**: Replace the single `<ProjectReference Include="..\SatelliteProvider.Services\SatelliteProvider.Services.csproj" />` with explicit references to the three new csprojs (whichever each test file actually needs — Tests likely needs all three; IntegrationTests likely needs none since it's HTTP-only). Update `using SatelliteProvider.Services;` → `using SatelliteProvider.Services.TileDownloader;` etc., one consumer at a time.
- **Rationale**: Tests must compile after C03.
- **Constraint Fit**: Test outcomes must remain identical (35/35 unit, 5/5 smoke).
- **Risk**: low — purely mechanical, surfaced by the compiler.
- **Dependencies**: C03.

### C05: Update `SatelliteProvider.Api` to reference the new Services projects and update DI

- **File(s)**: `SatelliteProvider.Api/SatelliteProvider.Api.csproj`, `SatelliteProvider.Api/Program.cs`, `SatelliteProvider.Api/Dockerfile` (if it explicitly COPYs `SatelliteProvider.Services/`)
- **Problem**: Same as C04 — `SatelliteProvider.Services.csproj` is gone after C03.
- **Change**: Add three `ProjectReference` entries. Update `using SatelliteProvider.Services;` to the three component namespaces. Update DI registrations: `AddSingleton<ISatelliteDownloader, GoogleMapsDownloaderV2>()` (unchanged FQN once `using` is fixed), same for `TileService`, `RegionService`, `RouteService`, `RegionRequestQueue`, `RegionProcessingService`, `RouteProcessingService`. Update `SatelliteProvider.Api/Dockerfile` to COPY the three new csproj directories instead of `SatelliteProvider.Services/`. Also update `SatelliteProvider.IntegrationTests/Dockerfile`.
- **Rationale**: API project must compile and the API container must build.
- **Constraint Fit**: HTTP API surface unchanged. Container behavior unchanged. Smoke must pass post-build.
- **Risk**: medium — Dockerfile changes are easy to miss; the smoke run will catch them. Solution-level `dotnet build` plus a fresh `docker compose build` is the safety check.
- **Dependencies**: C03.

### C06: Update module-layout.md, architecture.md, and refresh architecture_compliance_baseline.md

- **File(s)**: `_docs/02_document/module-layout.md`, `_docs/02_document/architecture.md`, `_docs/02_document/architecture_compliance_baseline.md`
- **Problem**: After C01..C05 land, the docs say something different than the code. Pre-existing F5 finding (DataAccess listed as importing Common but actually doesn't) is also stale.
- **Change**:
  - In `module-layout.md`, replace the `SatelliteProvider.Services` row with three rows (TileDownloader / RegionProcessing / RouteManagement), update the `Imports from` and `Allowed Dependencies` columns.
  - In `architecture.md`, update any layering diagram or text that still says "SatelliteProvider.Services".
  - Refresh `architecture_compliance_baseline.md`: mark F3 and F4 as **Resolved**, fix F5 (remove the wrong "Imports from: Common" row for DataAccess).
- **Rationale**: Doc-code parity. Keeps Phase 7 small and mechanical (it'll mostly be sanity-check).
- **Constraint Fit**: Docs only — no code or test impact.
- **Risk**: low.
- **Dependencies**: C05.

## Constraint-Rejected Alternatives (recorded per skill rule)

- **MediatR/CQRS layering**: would solve coupling more thoroughly but adds a heavy dependency for a small codebase, conflicts with the "simplest solution" coderule, and isn't required by any acceptance criterion. **Rejected**.
- **Move `ISatelliteDownloader` from Common into Services.TileDownloader**: structurally cleaner but breaks `RegionService` and `RegionProcessingService` consumers (they call the downloader directly). Would force every component to depend on TileDownloader's csproj — defeats the point of the split. **Rejected**.
- **Inline `IMemoryCache` into `ITileService` interface**: leaks an implementation choice (memory vs. distributed cache) into the public abstraction. **Rejected**.

## Self-Verification

- Every referenced file exists in the codebase (validated against `git ls-files` and the live tree).
- Each change has File(s), Problem, Change, Rationale, Constraint Fit, Risk, Dependencies.
- Component documentation already covers all affected areas (Targeted-mode skip applied).
- Logical-flow analysis: green smoke + unit run on 2026-05-10 traced every affected endpoint.
- Each change maps to ≤ 5 complexity points (C01: 3, C02: 2, C03: 5, C04: 2, C05: 3, C06: 2).

## Self-Verification (Phase 1 close)

Discovery summary:

- Two architecture findings to resolve (F3 Medium, F4 Medium).
- Six changes (C01..C06), one task per change after Phase 2 decomposition.
- Total estimate: ~17 story points, but each PBI stays at ≤ 5 points per the project's PBI sizing rule.

Ready for Phase 2 (Analysis + Roadmap + Task Decomposition) on user approval.