# Backend Phase 0 — Foundation, cross-cutting seams & starter cleanup

> **Mission:** turn the inherited starter skeleton into a clean Balinyaar foundation. Remove the demo
> scaffolding, stand up the **REST API surface** the marketplace needs (the baseline is gRPC-only today),
> wire the missing cross-cutting plumbing (rate limiting, request logging, current-user + audit-field
> stamping, PII encryption), and define every **mock-able external dependency as a DI seam** with a
> faithful in-memory implementation so later phases just plug real providers in. No domain tables yet —
> this phase makes the next fifteen phases possible and consistent.
>
> **Track:** backend · **Depends on:** nothing (first phase) · **Unlocks:** every backend phase
> **Before you start, read [`../_shared/agent-operating-rules.md`](../_shared/agent-operating-rules.md).** It is not optional.

---

## 1. Context — where this sits

This is the very first build phase. The server (`server/`, .NET 10, Clean Architecture, CQRS via
**`martinothamar/Mediator`** — *not* MediatR) already ships a working spine you must **keep and build
on**, and some demo scaffolding you must **remove**.

**What already exists (do not rebuild) — confirmed in the codebase:**
- ASP.NET Core Identity + **JWE/JWT** + **phone-OTP (passwordless TOTP)** auth, the dynamic-permission
  RBAC system, the CQRS pipeline (`ValidateCommandBehavior`, `MetricsBehaviour`), `OperationResult<T>`,
  Mapster, FluentValidation, Serilog, OpenTelemetry/prometheus, health checks, the `BaseController` +
  the full MVC filter/versioning/Swagger stack, and `Baya.Tests.Setup` (in-memory SQLite).
- The 12-project Clean-Arch solution `Baya.sln`. Identity tables map to the **`usr`** schema.

**What is starter scaffolding you will remove in this phase:**
- The `Order` demo end-to-end: `Domain/Entities/Order/Order.cs`, `Application/Features/Order/**`,
  `Contracts/Persistence/IOrderRepository.cs`, `Persistence/Repositories/OrderRepository.cs`,
  `Persistence/Configuration/OrderConfig/`, the `User.Orders` nav, `IUnitOfWork.OrderRepository`,
  `OrderGrpcServices` + `OrderGrpcServiceModels.proto` and its wiring.
- The three old migrations + snapshot (`Migrations/2021…`, `2022…`, `2023…`, namespace
  `Persistence.Migrations`) — they predate the marketplace and will be regenerated as the new baseline
  in **backend-phase-1**.

**Known gaps you will close here:** no HTTP controllers exist (REST surface must be created);
`LoggingBehavior<,>` is defined but never registered; **no rate limiting** is wired despite
`CONVENTIONS.md` §11; OTP delivery is stubbed (handled in b2).

## 2. Required reading (do this first)

- [`../_shared/agent-operating-rules.md`](../_shared/agent-operating-rules.md) and
  [`../_shared/backend-conventions-checklist.md`](../_shared/backend-conventions-checklist.md).
- [`server/CLAUDE.md`](../../../server/CLAUDE.md) — *Startup wiring*, *Project map*, *Identity & auth*,
  *Persistence*; and [`server/CONVENTIONS.md`](../../../server/CONVENTIONS.md) — §1 routing, §4
  controllers, §6 persistence (audit fields, soft delete), §9 logging, §11 security (rate limiting),
  §12 service registration.
- [`product/overview/platform-summary.md`](../../../product/overview/platform-summary.md) — the four
  ground truths (no cash custody, 10% VAT, full-upfront BNPL, weekly payout) and the **IRR-Rials-always**
  rule that shapes the money types you scaffold here.
- [`../../contracts/conventions/api-conventions.md`](../../contracts/conventions/api-conventions.md) and
  [`money-and-types.md`](../../contracts/conventions/money-and-types.md) — the envelope/format your REST
  surface must honour.
- Read the actual code you'll touch: `Baya.Web.Api/Program.cs`, `Baya.Application/Common/*Behavior*.cs`,
  `Baya.WebFramework/BaseController/*`, `Baya.Infrastructure.Persistence/ApplicationDbContext.cs`,
  and each project's `ServiceConfiguration/` extension.

## 3. Scope — build this

### 3.1 Remove the starter scaffolding
Delete the `Order` feature/entity/repository/config/gRPC/proto and the three old migrations listed in §1.
Remove every reference (nav property, `IUnitOfWork` member, DI wiring, proto compile). The solution must
build clean afterwards. Do **not** remove Identity/auth/observability — those stay.

### 3.2 Stand up the REST surface
The marketplace is REST/JSON. Create the first versioned controller(s) under `Baya.Web.Api/Controllers/V1/`
following `CONVENTIONS.md` §4 exactly (sealed, `BaseController`, inject `ISender`, `[controller]`/`[action]`
tokens, `base.OperationResult(...)`, `[Display(Description=...)]`). A minimal **health/ping** or
**reference** controller is enough to prove the pipeline end-to-end through Swagger; later phases add the
real controllers. Confirm `MapControllers()` now serves real routes and Swagger renders them.

### 3.3 Close the wiring gaps
- **Register `LoggingBehavior<,>`** in the Application pipeline (alongside the existing behaviors), in the
  correct order, so every request is structurally logged (no PII — `CONVENTIONS.md` §9).
- **Rate limiting** (`CONVENTIONS.md` §11): add `AddRateLimiter` via a `ServiceConfiguration/` extension
  and `UseRateLimiter()` **before** `UseAuthentication()` in `Program.cs`. Define named policies (a
  per-IP fixed-window/token-bucket baseline) ready for auth/OTP/refund/payout endpoints to apply later.

### 3.4 Current-user + audit-field stamping
- Add **`ICurrentUser`** (Application contract) wrapping the HTTP context (user id, roles), registered
  **Scoped**, with a null-object for non-HTTP contexts (jobs/tests).
- Add a **SaveChanges interceptor** (or extend the existing `SavingChanges` hook) that stamps
  `CreatedAt/ModifiedAt` and `CreatedById/ModifiedById` from `ICurrentUser` on a shared base/interface,
  per `CONVENTIONS.md` §6. Define the audit-capable base type (extend the existing `BaseEntity`/
  `ITimeModification` rather than inventing a parallel one). The `audit_logs` *table* and the
  append-only change log come in **b1** — here you build the *field-stamping* plumbing the interceptor
  needs and leave a clean extension point for b1 to add log-row writing.

### 3.5 Define the cross-cutting seams (interfaces + mocks + DI)
Create these **Application-layer interfaces** with real-shaped signatures, an **Infrastructure mock
implementation** each, and **DI registration** via a `ServiceConfiguration/` extension (selected by
config so a real impl swaps in later). Keep amounts as IRR `long`. This phase **introduces** these seams;
later phases reuse them.

- **`IDateTimeProvider`** — `DateTimeOffset UtcNow` (so time is testable; no `DateTime.Now` in handlers).
- **`IFieldEncryptor`** — `string Encrypt(string)` / `string Decrypt(string)` (+ a deterministic
  `Hash(string)` for lookups like `iban_hash`). Mock = local symmetric key from config; **never** log
  plaintext PII. This is what every encrypted PII column will use.
- **`ICacheService`** — typed get/set/remove with TTL and a `GetOrCreateAsync`. Mock = in-memory
  (`IMemoryCache`-backed). The config accessor (b1) and read-heavy queries cache through this; Redis later.
- **`IObjectStorage`** — presigned/streamed put/get/delete keyed by an opaque storage key, returning a
  retrievable URL. Mock = local-disk/in-memory store under a scratch path. MinIO/S3 later.
- **`INotificationDispatcher`** — `DispatchAsync(notification)` with a channel concept (in-app now;
  SMS/push later). Mock = no-op/log; the real in-app `notifications` write lands in b15. Define the seam
  now so emitting domains (booking, payments) can depend on it.

> The **OTP/SMS** seam (`ISmsSender`) is introduced in **b2** (with the auth REST surface), and the
> **search/payment/bnpl/bank/vendor** seams in their phases — do not pre-build those here; just leave the
> registry rows. (Listed in [mocks-registry.md](../../shared-working-context/reports/mocks-registry.md).)

### 3.6 Money & convention guardrails
Add (or confirm) the small shared helpers the money path will rely on: IRR is `long`/`BIGINT`
everywhere; if you add a money value object keep it integer-only with no float path. Document the rule in
`server/CONVENTIONS.md` if not already explicit.

## 4. Mocks & seams in this phase

| Seam | Mock behaviour | Registry |
| --- | --- | --- |
| `IDateTimeProvider` | returns real UTC now (deterministic override in tests) | n/a (not external) |
| `IFieldEncryptor` | local symmetric key from config; passthrough-but-reversible; deterministic hash | update row |
| `ICacheService` | in-memory `IMemoryCache` | update row |
| `IObjectStorage` | local-disk/in-memory blob store | update row |
| `INotificationDispatcher` | log/no-op (in-app write arrives in b15) | update row |

Record each in [`mocks-registry.md`](../../shared-working-context/reports/mocks-registry.md) (seam, file,
what's faked, config keys, how to make real, status 🟡).

## 5. Critical rules you must not get wrong

- **Don't break the working spine.** Identity, JWE auth, dynamic permissions, the CQRS behaviors, and
  observability must still work after cleanup. Run the existing tests.
- **Seams live in Application, implementations in Infrastructure.** Never reference Infrastructure from
  Application/Domain. Register via `ServiceConfiguration/` extensions called from `Program.cs` — no inline
  DI in `Program.cs` (`CONVENTIONS.md` §12).
- **Mock = real interface, fake body.** No `if (mock)` branches in handlers; selection is by registration.
- **`IFieldEncryptor` never leaks plaintext** into logs, exceptions, or non-`AsNoTracking` query
  projections of PII.
- **Audit-field stamping is infrastructure, not handler code** — handlers never set `CreatedById` etc.

## 6. Definition of Done

The shared [definition-of-done.md](../_shared/definition-of-done.md), plus:
- [ ] `Order` + the three old migrations are gone; `dotnet build Baya.sln` is clean (zero new warnings);
      `dotnet test Baya.sln` passes (existing identity tests still green).
- [ ] At least one real REST controller is reachable through Swagger and returns the `OperationResult`
      envelope.
- [ ] `LoggingBehavior` registered; rate limiter wired (policies defined, `UseRateLimiter` placed before
      auth).
- [ ] `ICurrentUser` + audit-field interceptor in place; the five seams (§3.5) registered with mocks.
- [ ] The **Project map** in `server/CLAUDE.md` is updated (Order removed; seams/cross-cutting noted);
      `CONVENTIONS.md` notes the IRR-`BIGINT` money rule if it wasn't explicit.

## 7. How to test (what a human can verify after this phase)

- `dotnet build Baya.sln` and `dotnet test Baya.sln` — both succeed.
- `dotnet run` the API → open `/swagger` → the new REST controller appears and its endpoint returns a
  200 in the standard envelope; the Order endpoints are gone.
- Hit the rate-limited test endpoint past its limit → `429`.
- A unit test proves `IFieldEncryptor.Decrypt(Encrypt(x)) == x` and the audit interceptor stamps
  `CreatedAt`/`CreatedById` on a save (use the SQLite test context).

## 8. Hand off & document (close the phase)

- **Docs:** update `server/CLAUDE.md` *Project map* (+ a one-line note on the new seams and where they're
  registered). Update `CONVENTIONS.md` only if you established a new rule.
- **Contracts:** no domain API yet — but publish the first `swagger.json` snapshot
  ([openapi/README.md](../../contracts/openapi/README.md)) so the frontend's f0 can wire its type pipeline
  against the envelope shape.
- **Handoff & report:** write `shared-working-context/backend/handoff/after-backend-phase-0.md` (the spine
  is clean, REST works, seams exist, what f0/b1 can rely on), append to `backend/STATUS.md`, write
  `reports/backend-phase-0-report.md`, and update `reports/mocks-registry.md` (the five rows → 🟡).
- **Memory:** save a `project` memory note for any non-obvious decision (e.g. how the seams are selected
  by config, the audit interceptor design) with a `MEMORY.md` pointer.
