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.
6.8 KiB
Backend Phase 0 — Foundation, cross-cutting seams & starter cleanup — Report (2026-06-28)
What was built
- Starter cleanup. Removed the
Orderdemo end-to-end: entity (Domain/Entities/Order), feature folder (Application/Features/Order),IOrderRepository/OrderRepository,OrderConfig,User.Ordersnav,IUnitOfWork.OrderRepository, the gRPCOrderGrpcServices+ proto + wiring +/GrpcUserOrdermap. Deleted the three pre-marketplace migrations (2021/2022/2023) + snapshot. - Fresh baseline migration
Migrations/20260628191947_InitialBaseline— Identity (usrschema) +UserRefreshTokens, with audit columnsCreatedAt/ModifiedAt(datetimeoffset),CreatedById/ModifiedById(int). NoOrderstable. - Audit base type.
BaseEntity/IAuditableEntityinDomain/Common/BaseEntity.cs:CreatedAt/ModifiedAtasDateTimeOffset,CreatedById/ModifiedByIdasint?(renamed from the oldCreatedTime/ModifiedDateDateTimepair, to matchCONVENTIONS.md§6). - Current-user plumbing.
ICurrentUser(Application/Contracts/Common) withHttpContextCurrentUser+NullCurrentUser(Infrastructure.Identity/Identity/CurrentUser/), registered Scoped;AddHttpContextAccessor()wired. - Audit interceptor.
AuditFieldInterceptor(Infrastructure.Persistence/Interceptors/), aSaveChangesInterceptorstamping audit fields fromICurrentUser+IDateTimeProvider. Replaces the oldDateTime.Nowdate hook inApplicationDbContext(the_cleanStringPersian-normalisation hook stays). Registered viaAddInterceptorsinAddPersistenceServices. - Five cross-cutting seams (interfaces in
Application/Contracts/Common, mocks inInfrastructure.CrossCutting/Seams/, registered byAddCrossCuttingSeams(config)):IDateTimeProvider→SystemDateTimeProvider,IFieldEncryptor→SymmetricFieldEncryptor,ICacheService→MemoryCacheService,IObjectStorage→LocalDiskObjectStorage,INotificationDispatcher→LogNotificationDispatcher. - Pipeline + rate limiting. Registered
LoggingBehavior<,>as the outermost Mediator behavior. AddedAddRateLimitingPolicies()(WebFramework/ServiceConfiguration) — built-in rate limiter with a per-IP global limit + named policiesotp/auth/sensitive;app.UseRateLimiter()placed beforeapp.UseAuthentication(). - REST surface.
Controllers/V1/PingController(sealed,BaseController,ISender) withGetStatusand a rate-limitedGetStatusRateLimited, backed by theFeatures/System/Queries/PingCQRS feature — proves REST → Mediator → behaviors →OperationResult→ApiResultenvelope. - Tests. New
Baya.Test.Foundationproject:SymmetricFieldEncryptorround-trip + deterministic hash,AuditFieldInterceptoradd/update stamping (SQLite),PingQueryHandlerhappy 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 theInitialBaselinemigration applied and the default users seeded.dotnet run --project src/API/Baya.Web.Api/...→ open/swagger:GET /api/v1/ping/get_status→200with body{ "data": { "service": "Baya.Web.Api", "status": "ok", "serverTimeUtc": "<utc>" }, "isSuccess": true, "statusCode": 200, "message": "Success", "requestId": "<trace>" }(the standardApiResult<T>envelope). ✅GET /api/v1/ping/get_status_rate_limitedfrom one IP → first 5 =200, then429. ✅- The Order REST/gRPC endpoints are gone; the swagger doc shows only the two ping paths. ✅
Run note: in non-Development the Serilog
MSSqlServersink targets thelogDbconnection (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/OperationResultenvelope shape. The first machineswagger.jsonsnapshot is published atdev/contracts/openapi/swagger.v1.json(pathsping/get_status+ping/get_status_rate_limited; schemasApiResult,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; CrossCuttingSeams/, PersistenceInterceptors/, IdentityCurrentUser/,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 IRRBIGINT— 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
AuditFieldInterceptorto also write append-onlyaudit_logsrows; evolve the baseline migration with the marketplace schema;IHolidayCalendarseed. - b2:
ISmsSenderseam + auth/OTP REST surface; apply theotp/authrate-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
MSSqlServersink points atlogDb=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.