backend phase 0: foundation, cross-cutting seams & starter cleanup

Remove the Order demo (entity/feature/repo/config/gRPC/proto) and the three
pre-marketplace migrations; regenerate a fresh InitialBaseline migration.

Stand up the REST surface (PingController + System/Ping CQRS) proving the
Mediator -> behaviors -> OperationResult -> ApiResult envelope end to end.

Close wiring gaps: register LoggingBehavior (outermost) and add the built-in
rate limiter (per-IP global + otp/auth/sensitive policies), placed before
authentication.

Add current-user + audit plumbing: ICurrentUser (HttpContext + null impls),
rename BaseEntity audit fields to CreatedAt/ModifiedAt (DateTimeOffset) +
CreatedById/ModifiedById, stamped by a new AuditFieldInterceptor.

Introduce five cross-cutting seams (IDateTimeProvider, IFieldEncryptor,
ICacheService, IObjectStorage, INotificationDispatcher) with in-memory/local
mocks registered via AddCrossCuttingSeams.

Add Baya.Test.Foundation (encryptor, audit interceptor, ping handler) and
update docs, contracts (swagger.v1.json), handoff, report, and mocks registry.
This commit is contained in:
hamid
2026-06-30 22:48:41 +03:30
parent 53a40dc51d
commit 765cc632d5
75 changed files with 1539 additions and 1418 deletions
+13 -1
View File
@@ -12,4 +12,16 @@ One block per completed backend phase. Newest at the top. Backend lane writes he
- **Notes for frontend:** <anything load-bearing>
-->
_(no phases completed yet)_
## backend-phase-0 — Foundation, cross-cutting seams & starter cleanup — 2026-06-28
- **Shipped:** removed the `Order` demo (entity/feature/repo/config/gRPC) + 3 old migrations; fresh
`InitialBaseline` migration; REST surface (`PingController` + `System/Ping` CQRS); `ICurrentUser` +
`AuditFieldInterceptor`; five cross-cutting seams (`IDateTimeProvider`, `IFieldEncryptor`,
`ICacheService`, `IObjectStorage`, `INotificationDispatcher`) with mocks; `LoggingBehavior` +
rate limiter (per-IP global + `otp`/`auth`/`sensitive`).
- **Contracts:** `dev/contracts/openapi/swagger.v1.json` published (envelope + ping schemas).
- **Mocked:** the 5 seams above → 🟡 (see reports/mocks-registry.md).
- **Gate:** build clean (0 new warnings) / tests green (10 pass). Live API verified vs `192.168.100.14`
(migration applied + seeded; ping `200`; rate-limit `429`).
- **Handoff:** backend/handoff/after-backend-phase-0.md
- **Notes for frontend:** `ApiResult` envelope is fixed (camelCase body, snake_case URLs);
`GET /api/v1/ping/get_status` is live to wire types against; `429` on over-limit.
@@ -0,0 +1,45 @@
# After backend-phase-0 — what f0/b1 can rely on
**The spine is clean and REST works.** The inherited `Order` demo (entity/feature/repo/config/gRPC) and
the three pre-marketplace migrations are gone; Identity + JWE auth + dynamic permissions + the CQRS
behaviors + observability are untouched and the existing tests stay green. A real REST controller is
live and proves the full pipeline.
## What the frontend (f0) can rely on now
- **The response envelope is fixed.** Every endpoint returns the server's `ApiResult` envelope, not a
bare body. Success shape:
```json
{ "isSuccess": true, "statusCode": 0, "message": "Success", "requestId": "<trace>", "data": <T> }
```
Failure returns `400/401/403/404` with the same envelope (field-level errors for validation). This is
what `clientFetch`/`serverFetch` must unwrap. JSON casing is the server default (camelCase for the
envelope/body properties; **URL segments are snake_case**).
- **A working endpoint to wire the type pipeline against:**
`GET /api/v1/ping/get_status` → `ApiResult<{ service, status, serverTimeUtc }>`.
- **Rate limiting exists.** Over-limit requests get `429`; `get_status_rate_limited` demonstrates it
(first 5/10s OK, then 429).
- **swagger.json is published:** `dev/contracts/openapi/swagger.v1.json` (envelope schemas
`ApiResult`, `ApiResultStatusCode`, `PingQueryResult`). Generate frontend types from it.
## What b1 can rely on now
- **Audit base type:** `BaseEntity`/`IAuditableEntity` (`Domain/Common/BaseEntity.cs`) with
`CreatedAt`/`ModifiedAt` (`DateTimeOffset`) + `CreatedById`/`ModifiedById` (`int?`). New entities that
derive `BaseEntity` get audit stamping for free.
- **Audit interceptor:** `AuditFieldInterceptor` (`Infrastructure.Persistence/Interceptors/`) stamps
those fields on save from `ICurrentUser` + `IDateTimeProvider`. **b1 extends this** to also write the
append-only `audit_logs` rows — the stamping plumbing and the clean extension point are in place.
- **Cross-cutting seams (DI-registered, mocked):** `IDateTimeProvider`, `IFieldEncryptor` (use it for
every encrypted PII column — phone/national_id/IBAN/addresses/clinical notes; deterministic `Hash`
for `*_hash` lookup columns), `ICacheService` (cache `platform_configs` / read-heavy data through it),
`IObjectStorage`, `INotificationDispatcher`. Contracts in `Application/Contracts/Common`; mocks in
`Infrastructure.CrossCutting/Seams/`; registered by `AddCrossCuttingSeams`.
- **Money rule:** IRR is `long`/`BIGINT`, integer-only, no floats (CONVENTIONS §6).
- **Migration baseline:** `20260628191947_InitialBaseline`. Add the marketplace schema on top of it;
generate with the documented `dotnet ef migrations add` command.
- **Rate-limit policies** `otp`/`auth`/`sensitive` are defined and ready to apply via
`[EnableRateLimiting("...")]` on the relevant endpoints (b2 auth/OTP).
## Known caveat
Non-Development environments route Serilog to the `logDb` connection (`Server=sql_server2022`); if that
host isn't reachable, the API won't start there. Development logs to console/file and boots cleanly
against the configured `SqlServer` DB. This is pre-existing infra config, unrelated to phase-0 code.