# Backend Phase 0 — Foundation, cross-cutting seams & starter cleanup — Report (2026-06-28)

## What was built
- **Starter cleanup.** Removed the `Order` demo end-to-end: entity (`Domain/Entities/Order`), feature
  folder (`Application/Features/Order`), `IOrderRepository`/`OrderRepository`, `OrderConfig`,
  `User.Orders` nav, `IUnitOfWork.OrderRepository`, the gRPC `OrderGrpcServices` + proto + wiring +
  `/GrpcUserOrder` map. Deleted the three pre-marketplace migrations (`2021/2022/2023`) + snapshot.
- **Fresh baseline migration** `Migrations/20260628191947_InitialBaseline` — Identity (`usr` schema) +
  `UserRefreshTokens`, with audit columns `CreatedAt`/`ModifiedAt` (`datetimeoffset`),
  `CreatedById`/`ModifiedById` (`int`). No `Orders` table.
- **Audit base type.** `BaseEntity`/`IAuditableEntity` in `Domain/Common/BaseEntity.cs`:
  `CreatedAt`/`ModifiedAt` as `DateTimeOffset`, `CreatedById`/`ModifiedById` as `int?`
  (renamed from the old `CreatedTime`/`ModifiedDate` `DateTime` pair, to match `CONVENTIONS.md` §6).
- **Current-user plumbing.** `ICurrentUser` (`Application/Contracts/Common`) with
  `HttpContextCurrentUser` + `NullCurrentUser` (`Infrastructure.Identity/Identity/CurrentUser/`),
  registered Scoped; `AddHttpContextAccessor()` wired.
- **Audit interceptor.** `AuditFieldInterceptor` (`Infrastructure.Persistence/Interceptors/`), a
  `SaveChangesInterceptor` stamping audit fields from `ICurrentUser` + `IDateTimeProvider`. Replaces
  the old `DateTime.Now` date hook in `ApplicationDbContext` (the `_cleanString` Persian-normalisation
  hook stays). Registered via `AddInterceptors` in `AddPersistenceServices`.
- **Five cross-cutting seams** (interfaces in `Application/Contracts/Common`, mocks in
  `Infrastructure.CrossCutting/Seams/`, registered by `AddCrossCuttingSeams(config)`):
  `IDateTimeProvider`→`SystemDateTimeProvider`, `IFieldEncryptor`→`SymmetricFieldEncryptor`,
  `ICacheService`→`MemoryCacheService`, `IObjectStorage`→`LocalDiskObjectStorage`,
  `INotificationDispatcher`→`LogNotificationDispatcher`.
- **Pipeline + rate limiting.** Registered `LoggingBehavior<,>` as the outermost Mediator behavior.
  Added `AddRateLimitingPolicies()` (`WebFramework/ServiceConfiguration`) — built-in rate limiter with
  a per-IP global limit + named policies `otp`/`auth`/`sensitive`; `app.UseRateLimiter()` placed
  **before** `app.UseAuthentication()`.
- **REST surface.** `Controllers/V1/PingController` (sealed, `BaseController`, `ISender`) with
  `GetStatus` and a rate-limited `GetStatusRateLimited`, backed by the `Features/System/Queries/Ping`
  CQRS feature — proves REST → Mediator → behaviors → `OperationResult` → `ApiResult` envelope.
- **Tests.** New `Baya.Test.Foundation` project: `SymmetricFieldEncryptor` round-trip + deterministic
  hash, `AuditFieldInterceptor` add/update stamping (SQLite), `PingQueryHandler` happy path.

## What is now testable (and exactly how)
- **Build/test gate:** `dotnet build Baya.sln` → 0 errors, 0 new warnings; `dotnet test Baya.sln` →
  10 pass (4 existing identity + 6 new foundation). ✅ verified.
- **Live API:** ✅ verified end-to-end against SQL Server `192.168.100.14` (Development env). On boot the
  `InitialBaseline` migration applied and the default users seeded. `dotnet run --project
  src/API/Baya.Web.Api/...` → open `/swagger`:
  - `GET /api/v1/ping/get_status` → `200` with body
    `{ "data": { "service": "Baya.Web.Api", "status": "ok", "serverTimeUtc": "<utc>" },
    "isSuccess": true, "statusCode": 200, "message": "Success", "requestId": "<trace>" }`
    (the standard `ApiResult<T>` envelope). ✅
  - `GET /api/v1/ping/get_status_rate_limited` from one IP → first 5 = `200`, then `429`. ✅
  - The Order REST/gRPC endpoints are gone; the swagger doc shows only the two ping paths. ✅

> Run note: in non-Development the Serilog `MSSqlServer` sink targets the `logDb` connection
> (`Server=sql_server2022`), which must resolve or the host fails to build (pre-existing config,
> unrelated to this phase). Development logs to console/file, so verify there.

## What is mocked / waiting on a real service
All five seams are 🟡 (mock behind DI seam) — see `reports/mocks-registry.md` rows for
`IFieldEncryptor`, `ICacheService`, `IObjectStorage`, `INotificationDispatcher` (and `IDateTimeProvider`,
not external). Each mock lives in `Baya.Infrastructure.CrossCutting/Seams/`; swapping to a real provider
is a registration change in `AddCrossCuttingSeams`. `INotificationDispatcher` does **not** write yet —
the in-app `notifications` write lands in b15.

## Contracts
- **Produced:** the `ApiResult`/`OperationResult` envelope shape. The first machine `swagger.json`
  snapshot is published at `dev/contracts/openapi/swagger.v1.json` (paths `ping/get_status` +
  `ping/get_status_rate_limited`; schemas `ApiResult`, `ApiResultStatusCode`,
  `ApiResultOfPingQueryResult`, `PingQueryResult`). Casing on the wire: **camelCase** body/envelope
  properties, **snake_case** URL segments.
- **Consumed:** none.

## Docs updated
- `server/CLAUDE.md` — *Project map* (Order removed; CrossCutting `Seams/`, Persistence `Interceptors/`,
  Identity `CurrentUser/`, `Baya.Test.Foundation`, new seams note), *Startup wiring* (new registrations
  + rate-limiter-before-auth pipeline order), CQRS example (Order → generic + Ping).
- `server/CONVENTIONS.md` — §6 "as built" note (audit base type + interceptor) and a new
  "Money is IRR `BIGINT` — integer-only, no floats" rule.
- `dev/shared-working-context/reports/mocks-registry.md` — five seam rows → 🟡 with files + config keys.

## Follow-ups for later phases
- **b1:** extend `AuditFieldInterceptor` to also write append-only `audit_logs` rows; evolve the
  baseline migration with the marketplace schema; `IHolidayCalendar` seed.
- **b2:** `ISmsSender` seam + auth/OTP REST surface; apply the `otp`/`auth` rate-limit policies to those
  endpoints.
- **Integration tests:** a `WebApplicationFactory<Program>` test project (CONVENTIONS §10) was not
  scaffolded this phase; add it when the first real feature area lands so the HTTP pipeline (routing,
  auth, envelope translation, 429) is covered automatically.
- **Logging config (pre-existing):** the non-Development Serilog `MSSqlServer` sink points at
  `logDb` = `Server=sql_server2022`. If that host isn't reachable in an environment, the API won't
  start there. Out of scope for this phase — flag for whoever owns environment/infra config.

## Status
All Definition-of-Done items met: build clean (0 new warnings), 10 tests green, REST controller live
through Swagger with the `OperationResult` envelope, `LoggingBehavior` + rate limiter wired,
`ICurrentUser` + audit interceptor + the five seams in place, migration applied + DB seeded, docs/
contracts/handoff/registry updated, swagger snapshot published. Live verification done against
`192.168.100.14`.
