Files
hamid 765cc632d5 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.
2026-06-30 22:48:41 +03:30

46 lines
3.1 KiB
Markdown

# 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.