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>
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/:
IPlatformConfig—GetConfig<T>(key)(cached, parsed bydata_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 callsNextBusinessDay.IAuditLogger—WriteAsync(entityType, entityId, action, changedFields?)for state changes with no row diff, plusGetTrailAsync. Row-level diffs onIAuditableentities are written automatically by the extendedAuditFieldInterceptor(mark an entityIAuditable; annotate encrypted props[AuditRedacted]).platform_configsis the firstIAuditableentity.audit_logsis append-only — never update/delete it.IAnalyticsSink—EmitAsync(name, props); fire-and-forget (system_events). Never route compliance facts here — those go toIAuditLogger.INotificationDispatcher—DispatchAsync(Notification(userId, type, title, body?, dataJson?))now writes a real in-appnotificationsrow (b0 stub gone). This is how booking/payment/review domains mint a user notification.data_jsonis 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 toICurrentUser.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 usernotification.
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) addsFK SupportAlerts.BookingId → Bookings; b14 (reviews) addsFK SupportAlerts.ReviewId → Reviews. Do it in the migration that creates those tables. - Make-it-real seams (🟡):
IHolidayCalendar(real feed/sync),IAnalyticsSink(warehouse),IJobSchedulerretention (Hangfire/Quartz),INotificationDispatcherSMS/push channels — seereports/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).