# Module Layout **Status**: derived-from-code (post-rename, forward-looking — see Verification Needed) **Language**: csharp **Layout Convention**: **custom** (layer-organized, NOT per-component-directory — see `## Verification Needed` below) **Root**: `./` (no `src/` directory; .NET `Microsoft.NET.Sdk.Web` project at the repo root) **Last Updated**: 2026-05-14 > **NOTE (forward-looking)**: file paths reflect the post-rename + post-GPS-Denied-removal state. Today's source still uses `Aircraft*` / `Flight*` / `Orthophoto*` / `GpsCorrection*` filenames, csproj is `Azaion.Flights.csproj`, and namespace is `Azaion.Flights.*`. Renames + drops are tracked under Jira AZ-EPIC children B5 (namespace), B6 (rename), B7 (GPS-Denied removal), B8 (HTTP routes), B9 (DB migration), B10 (Dockerfile / image / compose). ## Layout Rules This codebase **does not** follow the "one directory per component" convention from the template. It is organized **horizontally** by architectural layer: ``` ./ ├── Auth/ ← cross-cutting (component 05_identity) ├── Controllers/ ← API surface (one file per feature component, 01 + 02) ├── Database/ ← persistence (component 04) │ └── Entities/ ├── DTOs/ ← payload types (split across components 01, 02, 06) ├── Enums/ ← domain enums (split across components 01, 02, 04) ├── Middleware/ ← cross-cutting (component 06_http_conventions) ├── Services/ ← business logic (one file per feature component, 01 + 02) ├── Entities/ ← EMPTY (scaffolding leftover) ├── Infrastructure/ ← EMPTY (scaffolding leftover) ├── Program.cs ← composition root (component 07_host) └── GlobalUsings.cs ← composition root (component 07_host) ``` Consequence: each component's `Owns` glob is a SET OF FILE PATHS spanning multiple directories, NOT a single directory glob. There is no `shared/` directory; cross-cutting concerns (`05_identity`, `06_http_conventions`) own their root-level dirs (`Auth/`, `Middleware/`) directly. The C# project has no separate per-component csproj — there is one project (post-rename: `Azaion.Missions.csproj`; today: `Azaion.Flights.csproj`) and effectively one root namespace (post-rename: `Azaion.Missions.*`). Other components reference each other through types directly; there is no compiled "Public API" boundary. ## Per-Component Mapping ### Component: 01_vehicle_catalog - **Epic**: Jira AZ-EPIC (rename + multi-vehicle support) - **Directory(ies)**: spread across `Controllers/`, `Services/`, `DTOs/`, `Enums/` - **Public API** (types other components reference): `Services/VehicleService.cs` (consumed by `07_host` for DI registration; `02_mission_planning` consumes its existence semantics through the DB) - **Owns (exclusive write)** (post-rename): - `Controllers/VehiclesController.cs` - `Services/VehicleService.cs` - `DTOs/CreateVehicleRequest.cs` - `DTOs/UpdateVehicleRequest.cs` - `DTOs/GetVehiclesQuery.cs` - `DTOs/SetDefaultRequest.cs` - **Internal**: none — every file is publicly importable in C# without explicit visibility annotations - **Imports from**: `04_persistence` (`AppDataConnection`, `Vehicle` entity, `VehicleType` enum, `FuelType` enum), `05_identity` (`[Authorize(Policy = "FL")]`), `06_http_conventions` (exception → middleware mapping is implicit) - **Consumed by**: `02_mission_planning` (FK existence-check on `vehicle_id`), `07_host` (DI registration) ### Component: 02_mission_planning - **Epic**: Jira AZ-EPIC - **Directory(ies)**: spread across `Controllers/`, `Services/`, `DTOs/`, `Enums/` - **Public API** (post-rename): `Services/MissionService.cs`, `Services/WaypointService.cs` (DI-registered in `07_host`) - **Owns (exclusive write)** (post-rename): - `Controllers/MissionsController.cs` - `Services/MissionService.cs` - `Services/WaypointService.cs` - `DTOs/CreateMissionRequest.cs` - `DTOs/UpdateMissionRequest.cs` - `DTOs/GetMissionsQuery.cs` - `DTOs/CreateWaypointRequest.cs` - `DTOs/UpdateWaypointRequest.cs` - `DTOs/GeoPoint.cs` - **Internal**: none - **Imports from**: `04_persistence` (incl. `WaypointSource` + `WaypointObjective` enums), `05_identity`, `06_http_conventions` (`PaginatedResponse`), `01_vehicle_catalog` (existence semantics through DB FK) - **Consumed by**: `07_host` (DI), and external `autopilot` / `ui` (cross-service over HTTP) ### Component: 04_persistence - **Epic**: Jira AZ-EPIC - **Directory(ies)**: `Database/`, plus `Enums/ObjectStatus.cs` (cross-cutting status enum) - **Public API**: `Database/AppDataConnection.cs` (the `DataConnection` type other components depend on), `Database/DatabaseMigrator.cs` (called by `07_host` at startup), all 7 entities under `Database/Entities/` (referenced by services + DTOs as row maps), the four persisted-column enums under `Enums/` (`VehicleType`, `FuelType`, `WaypointSource`, `WaypointObjective`, `ObjectStatus`) - **Owns (exclusive write)** (post-rename): - `Database/AppDataConnection.cs` - `Database/DatabaseMigrator.cs` - `Database/Entities/Vehicle.cs` - `Database/Entities/Mission.cs` - `Database/Entities/Waypoint.cs` - `Database/Entities/MapObject.cs` - `Database/Entities/Media.cs` (borrowed schema) - `Database/Entities/Annotation.cs` (borrowed schema) - `Database/Entities/Detection.cs` (borrowed schema) - `Enums/VehicleType.cs` (persisted on `vehicles.type`; consumed by `01_vehicle_catalog` DTOs) - `Enums/FuelType.cs` (persisted on `vehicles.fuel_type`; consumed by `01_vehicle_catalog` DTOs) - `Enums/WaypointSource.cs` (persisted on `waypoints.waypoint_source`; consumed by `02_mission_planning` DTOs) - `Enums/WaypointObjective.cs` (persisted on `waypoints.waypoint_objective`; consumed by `02_mission_planning` DTOs) - `Enums/ObjectStatus.cs` (persisted on `map_objects.object_status`) - **Internal**: none - **Imports from**: nothing internal - **Consumed by**: `01_vehicle_catalog`, `02_mission_planning`, `07_host` ### Component: 05_identity - **Epic**: Jira AZ-EPIC - **Directory(ies)**: `Auth/` - **Public API**: `Auth/JwtExtensions.AddJwtAuth(...)` (called by `07_host`); the `"FL"` policy NAME (referenced as a string by feature controllers — string-typed dependency, NOT compile-checked) - **Owns (exclusive write)**: - `Auth/JwtExtensions.cs` - **Internal**: none - **Imports from**: nothing internal - **Consumed by**: `01_vehicle_catalog`, `02_mission_planning`, `07_host` ### Component: 06_http_conventions - **Epic**: Jira AZ-EPIC - **Directory(ies)**: `Middleware/`, plus `DTOs/ErrorResponse.cs` and `DTOs/PaginatedResponse.cs` - **Public API**: `Middleware/ErrorHandlingMiddleware` (registered by `07_host`); `DTOs/PaginatedResponse` (returned by `02_mission_planning`); `DTOs/ErrorResponse` is unused on the wire today (see component description Caveats #2) - **Owns (exclusive write)**: - `Middleware/ErrorHandlingMiddleware.cs` - `DTOs/ErrorResponse.cs` - `DTOs/PaginatedResponse.cs` - **Internal**: none - **Imports from**: nothing internal - **Consumed by**: `02_mission_planning` (`PaginatedResponse`), `07_host` (middleware registration), all components implicitly (exception → status code mapping) ### Component: 07_host - **Epic**: Jira AZ-EPIC - **Directory(ies)**: repo root - **Public API**: none (it is the runtime entry point) - **Owns (exclusive write)**: - `Program.cs` - `GlobalUsings.cs` - **Internal**: none - **Imports from**: `04_persistence`, `05_identity`, `06_http_conventions`, `01_vehicle_catalog`, `02_mission_planning` - **Consumed by**: nothing internal — invoked by the .NET runtime via `dotnet Azaion.Missions.dll` (post-rename) / `dotnet Azaion.Flights.dll` (today) ## Shared / Cross-Cutting There is no `shared/` directory in this codebase. The role canonically taken by `shared/*` is filled by: - **`05_identity`** (`Auth/`) — auth setup + named policies - **`06_http_conventions`** (`Middleware/` + 2 DTOs) — error envelope + paginated response envelope - **`04_persistence`** — provides shared `AppDataConnection` to all feature components All five enums under `Enums/` (`VehicleType`, `FuelType`, `WaypointSource`, `WaypointObjective`, `ObjectStatus`) are owned by `04_persistence` because they are *persisted column types* — every one of them maps to an `INTEGER` column in the schema (`Database/DatabaseMigrator.cs`) and is referenced from a `04_persistence`-owned entity (`Vehicle`, `Waypoint`, `MapObject`). Feature components (`01`, `02`) consume them as foundation types, never own them. This was retagged on 2026-05-14 to resolve baseline findings F1 + F2 (see `_docs/02_document/architecture_compliance_baseline.md`); previously `VehicleType` / `FuelType` were tagged under `01` and `WaypointSource` / `WaypointObjective` under `02`, which created a Foundation ← Feature layering violation. ## Allowed Dependencies (layering) Read top-to-bottom; an upper layer may import from a lower layer but NEVER the reverse. | Layer | Components | May import from | |-------|------------|-----------------| | 4. Composition root | 07_host | 1, 2, 3 | | 3. Feature surfaces | 01_vehicle_catalog, 02_mission_planning | 1, 2 | | 2. Domain (none today) | — | 1 | | 1. Foundation | 04_persistence, 05_identity, 06_http_conventions | (none) | There is no "Domain" layer in this codebase — feature components are thin (controller + service + DTOs) and bind directly to persistence entities. This matches typical CRUD-style ASP.NET Core services and is the deliberate shape per `../../suite/_docs/02_missions.md`. Violations of this table are **Architecture** findings in code-review Phase 7 (High severity). ## Layout Conventions (reference) | Language | Root | Per-component path | Public API file | Test path | |----------|------|-------------------|-----------------|-----------| | C# (.NET) | `src/` (canonical) | `src//` | `src//.cs` (namespace root) | `tests/.Tests/` | | **C# (this repo)** | `./` (NOT canonical) | spread by horizontal layer | (no per-component public-API file; types referenced directly) | **no tests project** | ## Verification Needed - [ ] **Forward-looking file paths**: every "Owns" path above reflects the post-rename target (B5/B6/B7/B8). Today the files still have `Aircraft*` / `Flight*` / `Orthophoto*` / `GpsCorrection*` names. Implementers of B5–B10 should treat this layout as the spec for the rename, not the current ground truth. After B6 ships the layout matches the disk. - [ ] **Layer-organized vs component-organized layout**: this codebase organizes files by horizontal layer (`Controllers/`, `Services/`, `DTOs/`, `Enums/`) not by feature component. The Owns globs are composed from multiple directories, which is unusual and means a single directory rename would touch multiple components' Owns. **Question for user**: keep as-is (matches the rest of the suite's .NET services? — needs verification against `annotations` and `admin` layout) or should a future refactor move toward feature-folders? - [ ] **`Entities/` and `Infrastructure/` at the root are EMPTY** — `Entities/` is shadowed by `Database/Entities/`. With GPS-Denied moving out of this repo, the historical "earmarked for orthophoto path resolver" reason is gone. **Question for user**: delete both empty dirs as part of B5? - [ ] **No `src/` directory** — the .NET project sits at the repo root. `coderule.mdc` says "For existing projects, follow the established directory structure." → established structure is "no `src/`"; this layout DOC respects that. Confirm we should NOT move it. - [ ] **Policy name is string-typed** — feature controllers reference `"FL"` as a raw string. A typo would silently turn into a permanent 403. **Question for user**: should `05_identity` expose a typed `PolicyNames.FL` constant? Cheap improvement; not a blocker for documentation. - [ ] **Cross-component DTO clusters**: `DTOs/` directory mixes payloads from `01`, `02`, and `06`. Owns globs are file-by-file. Acceptable for now; a future refactor could split into per-component subfolders (e.g. `DTOs/Vehicle/`, `DTOs/Mission/`, `DTOs/Common/`). - [ ] **No `tests/` project exists** (per `../../suite/_docs/_process_leftovers/2026-04-22_ci-unit-test-lane-missing-projects.md`). Test-spec / test-implement steps in autodev will need to create a sibling `Azaion.Missions.Tests` csproj — at `tests/Azaion.Missions.Tests/` (suite-canonical) or somewhere else? Confirm. - [ ] **Cycles spanning components**: none detected. The `Vehicle → Mission → Waypoint` association graph is intra-component (entirely inside `04_persistence`).