# 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": "" }, "isSuccess": true, "statusCode": 200, "message": "Success", "requestId": "" }` (the standard `ApiResult` 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` 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`.