Files
baya-monorepo/dev/shared-working-context/backend/handoff/after-backend-phase-1.md
T
hamid 2f2aec61a2 backend phase 1: config, reference & platform signals
Lay the cross-cutting platform backbone every later phase reads from. Adds
the first marketplace EF migration baseline (new `ops` schema) and the
mechanisms b2..b15 reuse: typed runtime config, an append-only audit trail,
an analytics event log, the holiday/bank-closure calendar, in-app
notifications, and the internal support-alert worklist.

Schema & migration
- New `ops` schema + migration InitialMarketplaceBaseline with 6 tables:
  PlatformConfigs (IAuditable), AuditLogs (append-only), SystemEvents,
  IranianHolidays, Notifications, SupportAlerts — with indexes/uniques and
  FKs to usr.Users. Seeded 12 config keys + 7 sample holidays via HasData.

Domain / Application
- IAuditable marker + [AuditRedacted] attribute; entities + string-code
  constant holders (config data_type, holiday type, alert type/severity/status).
- Facade contracts: IPlatformConfig, IHolidayCalendar, IAnalyticsSink,
  IAuditLogger, INotificationService, ISupportAlertService; DTOs +
  PagedResult<T>; evolved the INotificationDispatcher.Notification record to
  carry Type + DataJson; Pagination helper.
- 14 CQRS commands/queries (+ validators) wiring the endpoints to the facades.

Infrastructure
- DB-backed facade implementations in Persistence/Services/; real in-app
  INotificationDispatcher (removes the b0 log stub); notification-retention
  hosted service (purge is_read=1 AND age>90d).
- Extended AuditFieldInterceptor to also append an old/new-diff audit_logs row
  for every IAuditable change in the same transaction (PII redacted).
- Registered all facades + hosted service in AddPersistenceServices; removed
  the dispatcher registration from AddCrossCuttingSeams.

API
- 5 controllers: admin PlatformConfig/Holidays/Audit/SupportAlerts
  ([Authorize(DynamicPermission)]) + current-user Notifications ([Authorize]),
  all tenant-scoped and paginated. 16 Swagger paths total.

Money-correctness & safety rules honoured
- Config read at compute time (cached, parsed by data_type), never hardcoded;
  every config change is audited in the same transaction; audit_logs is
  append-only (no update/delete path); support alerts are admin-only;
  notifications are tenant-scoped; analytics is fire-and-forget.

Tests & docs
- 18 new foundation tests over in-memory SQLite (config typing + audit,
  holidays, notifications + tenancy + retention, support alerts, analytics);
  build clean (0 new code warnings), 22 tests green; migration applied to the
  dev DB and swagger.v1.json refreshed.
- Updated server Project map + CONVENTIONS, product data-model doc 12 (seeded
  config defaults), config-reference contract, mock registry, backend handoff/
  STATUS/report.

Follow-ups: add FK constraints for SupportAlerts.BookingId (b9) and ReviewId
(b14) when those tables land.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 01:18:00 +03:30

4.3 KiB

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/:

  • IPlatformConfigGetConfig<T>(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.
  • IHolidayCalendarIsHoliday, IsBankClosed, NextBusinessDay (skips bank-closed days + the Iranian banking weekend = Friday), plus admin CRUD. b13 payout scheduling calls NextBusinessDay.
  • IAuditLoggerWriteAsync(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.
  • IAnalyticsSinkEmitAsync(name, props); fire-and-forget (system_events). Never route compliance facts here — those go to IAuditLogger.
  • INotificationDispatcherDispatchAsync(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.
  • ISupportAlertServiceRaiseAsync(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).