# After backend-phase-1 — what b2…b15 and the frontend can rely on The **platform backbone** is live. Six cross-cutting tables exist in a new **`ops` schema** on top of b0's `InitialBaseline`, seeded with real config + a sample holiday calendar. The mechanisms every later phase needs — typed config, holiday math, audit trail, analytics, in-app notifications, support alerts — are built **once, here**, behind Application contracts. **Reuse them; do not re-create the tables.** ## Internal contracts b2…b15 must depend on (never reinvent) All are DI-registered (Scoped) and implemented in `Baya.Infrastructure.Persistence/Services/`: - **`IPlatformConfig`** — `GetConfig(key)` (cached, parsed by `data_type`), `SetConfig(key,value)` (audited, evicts cache), `ListAsync`, `GetConfigChangeHistory(key)`. **Read money-critical constants here at compute time — never hardcode.** Seeded keys: `platform_fee_rate`, `vat_rate` (0.10), `dispute_window_hours` (72), `booking_payment_deadline_minutes` (30), `nurse_response_deadline_hours`, `nurse_payout_interval_days`, `evv_location_tolerance_meters`, `min_rating_for_support_alert`, `bnpl_merchant_of_record`, `bnpl_provider_commission_rate`, `bnpl_settlement_timing`, `cancellation_tiers`. **Snapshot the rate you use onto the priced row** — a later config change must not re-price it. - **`IHolidayCalendar`** — `IsHoliday`, `IsBankClosed`, `NextBusinessDay` (skips bank-closed days + the Iranian banking weekend = Friday), plus admin CRUD. **b13 payout scheduling calls `NextBusinessDay`.** - **`IAuditLogger`** — `WriteAsync(entityType, entityId, action, changedFields?)` for state changes with no row diff, plus `GetTrailAsync`. Row-level diffs on **`IAuditable`** entities are written automatically by the extended `AuditFieldInterceptor` (mark an entity `IAuditable`; annotate encrypted props `[AuditRedacted]`). `platform_configs` is the first `IAuditable` entity. **`audit_logs` is append-only — never update/delete it.** - **`IAnalyticsSink`** — `EmitAsync(name, props)`; fire-and-forget (`system_events`). **Never** route compliance facts here — those go to `IAuditLogger`. - **`INotificationDispatcher`** — `DispatchAsync(Notification(userId, type, title, body?, dataJson?))` now writes a **real in-app `notifications` row** (b0 stub gone). This is how booking/payment/review domains mint a user notification. `data_json` is a **typed, versioned deep-link payload** — version it. - **`INotificationService`** — per-user reads/commands (list unread-first, unread count, mark read/all, purge). Always tenant-scoped to `ICurrentUser`. - **`ISupportAlertService`** — `RaiseAsync(type, entityType, entityId, severity, bookingId?, reviewId?)` for review/EVV/verification/payment flows to call, plus assign/resolve/list. **Support alerts are admin-only — never surface them on a user-facing route or in a user `notification`.** ## Live endpoints (contract: `dev/contracts/domains/config-reference.md`) Admin (`DynamicPermission`): `platform_config/*`, `holidays/*`, `audit/get_audit_trail`, `support_alerts/*`. Current-user (`Authorize`): `notifications/*` (f14 notification center; f15 admin config/holidays/audit/alerts). Envelope unchanged (camelCase body, snake_case URLs); lists paginated `page`/`page_size`. ## Migration / schema New migration **`20260701193257_InitialMarketplaceBaseline`** — the marketplace baseline every later phase adds onto. Applied cleanly to the dev DB (tables + indexes + seed present). Tables live in **`ops`** (keep new marketplace tables off `usr`). ## Follow-ups later phases must close - **FK constraints for `support_alerts.booking_id` / `review_id`.** The columns exist now (no FK yet). **b9** (bookings) adds `FK SupportAlerts.BookingId → Bookings`; **b14** (reviews) adds `FK SupportAlerts.ReviewId → Reviews`. Do it in the migration that creates those tables. - **Make-it-real seams (🟡):** `IHolidayCalendar` (real feed/sync), `IAnalyticsSink` (warehouse), `IJobScheduler` retention (Hangfire/Quartz), `INotificationDispatcher` SMS/push channels — see `reports/mocks-registry.md`. ## Caveat (unchanged from b0) Non-Development Serilog targets the `logDb` connection; Development boots against the configured `SqlServer`. A reachable SQL Server is required to run the API (it applies migrations + seeds on boot).